#include "UsgsAstroFramePlugin.h" #include "UsgsAstroFrameSensorModel.h" #include #include #include #include #include #include #include #include #include #include #include #include using json = nlohmann::json; #ifdef _WIN32 # define DIR_DELIMITER_STR "\\" #else # define DIR_DELIMITER_STR "/" #endif // Declaration of static variables const std::string UsgsAstroFramePlugin::_PLUGIN_NAME = "UsgsAstroFramePluginCSM"; const std::string UsgsAstroFramePlugin::_MANUFACTURER_NAME = "UsgsAstrogeology"; const std::string UsgsAstroFramePlugin::_RELEASE_DATE = "20170425"; const int UsgsAstroFramePlugin::_N_SENSOR_MODELS = 1; const int UsgsAstroFramePlugin::_NUM_ISD_KEYWORDS = 36; const std::string UsgsAstroFramePlugin::_ISD_KEYWORD[] = { "boresight", "ccd_center", "ephemeris_time", "focal_length", "focal_length_epsilon", "ifov", "instrument_id", "itrans_line", "itrans_sample", "kappa", "min_elevation", "max_elevation", "model_name", "nlines", "nsamples", "odt_x", "odt_y", "omega", "original_half_lines", "original_half_samples", "phi", "pixel_pitch", "semi_major_axis", "semi_minor_axis", "spacecraft_name", "starting_detector_line", "starting_detector_sample", "target_name", "transx", "transy", "x_sensor_origin", "y_sensor_origin", "z_sensor_origin", "x_sensor_velocity", "y_sensor_velocity", "z_sensor_velocity", "x_sun_position", "y_sun_position", "z_sun_position" }; const int UsgsAstroFramePlugin::_NUM_STATE_KEYWORDS = 32; const std::string UsgsAstroFramePlugin::_STATE_KEYWORD[] = { "m_focalLength", "m_iTransS", "m_iTransL", "m_boresight", "m_transX", "m_transY", "m_majorAxis", "m_minorAxis", "m_spacecraftVelocity", "m_sunPosition", "m_startingDetectorSample", "m_startingDetectorLine", "m_targetName", "m_ifov", "m_instrumentID", "m_focalLengthEpsilon", "m_ccdCenter", "m_line_pp", "m_sample_pp", "m_minElevation", "m_maxElevation", "m_odtX", "m_odtY", "m_originalHalfLines", "m_originalHalfSamples", "m_spacecraftName", "m_pixelPitch", "m_ephemerisTime", "m_nLines", "m_nSamples", "m_currentParameterValue", "m_currentParameterCovariance" }; // Static Instance of itself const UsgsAstroFramePlugin UsgsAstroFramePlugin::m_registeredPlugin; UsgsAstroFramePlugin::UsgsAstroFramePlugin() { } UsgsAstroFramePlugin::~UsgsAstroFramePlugin() { } std::string UsgsAstroFramePlugin::getPluginName() const { return _PLUGIN_NAME; } std::string UsgsAstroFramePlugin::getManufacturer() const { return _MANUFACTURER_NAME; } std::string UsgsAstroFramePlugin::getReleaseDate() const { return _RELEASE_DATE; } csm::Version UsgsAstroFramePlugin::getCsmVersion() const { return CURRENT_CSM_VERSION; } size_t UsgsAstroFramePlugin::getNumModels() const { return _N_SENSOR_MODELS; } std::string UsgsAstroFramePlugin::getModelName(size_t modelIndex) const { return UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME; } std::string UsgsAstroFramePlugin::getModelFamily(size_t modelIndex) const { return CSM_RASTER_FAMILY; } csm::Version UsgsAstroFramePlugin::getModelVersion(const std::string &modelName) const { return csm::Version(1, 0, 0); } bool UsgsAstroFramePlugin::canModelBeConstructedFromState(const std::string &modelName, const std::string &modelState, csm::WarningList *warnings) const { bool constructible = true; // Get the model name from the model state std::string model_name_from_state; try { model_name_from_state = getModelNameFromModelState(modelState, warnings); } catch(...) { return false; } // Check that the plugin supports the model if (modelName != model_name_from_state || modelName != UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME){ constructible = false; } // 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()){ constructible = false; break; } } return constructible; } bool UsgsAstroFramePlugin::canModelBeConstructedFromISD(const csm::Isd &imageSupportData, const std::string &modelName, csm::WarningList *warnings) const { return canISDBeConvertedToModelState(imageSupportData, modelName, warnings); } csm::Model *UsgsAstroFramePlugin::constructModelFromState(const std::string& modelState, csm::WarningList *warnings) const { csm::Model *sensor_model = 0; // Get the sensor model name from the sensor model state std::string model_name_from_state = getModelNameFromModelState(modelState); if (model_name_from_state != UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME){ csm::Error::ErrorType aErrorType = csm::Error::INVALID_SENSOR_MODEL_STATE; std::string aMessage = "Model name from state is not recognized."; std::string aFunction = "UsgsAstroFramePlugin::constructModelFromState()"; throw csm::Error(aErrorType, aMessage, aFunction); } if (!canModelBeConstructedFromState(model_name_from_state, modelState)){ csm::Error::ErrorType aErrorType = csm::Error::INVALID_SENSOR_MODEL_STATE; std::string aMessage = "Model state is not valid."; std::string aFunction = "UsgsAstroFramePlugin::constructModelFromState()"; throw csm::Error(aErrorType, aMessage, aFunction); } // Create the model from the state UsgsAstroFrameSensorModel* mdsensor_model = new UsgsAstroFrameSensorModel(); auto state = json::parse(modelState); mdsensor_model->m_ccdCenter[0] = state["m_ccdCenter"][0]; mdsensor_model->m_ccdCenter[1] = state["m_ccdCenter"][1]; mdsensor_model->m_ephemerisTime = state["m_ephemerisTime"]; mdsensor_model->m_focalLength = state["m_focalLength"]; mdsensor_model->m_focalLengthEpsilon = state["m_focalLengthEpsilon"]; mdsensor_model->m_ifov = state["m_ifov"]; mdsensor_model->m_instrumentID = state["m_instrumentID"]; mdsensor_model->m_majorAxis = state["m_majorAxis"]; mdsensor_model->m_minorAxis = state["m_minorAxis"]; mdsensor_model->m_startingDetectorLine = state["m_startingDetectorLine"]; mdsensor_model->m_startingDetectorSample = state["m_startingDetectorSample"]; mdsensor_model->m_line_pp = state["m_line_pp"]; mdsensor_model->m_sample_pp = state["m_sample_pp"]; mdsensor_model->m_originalHalfLines = state["m_originalHalfLines"]; mdsensor_model->m_originalHalfSamples = state["m_originalHalfSamples"]; mdsensor_model->m_spacecraftName = state["m_spacecraftName"]; mdsensor_model->m_pixelPitch = state["m_pixelPitch"]; mdsensor_model->m_nLines = state["m_nLines"]; mdsensor_model->m_nSamples = state["m_nSamples"]; mdsensor_model->m_minElevation = state["m_minElevation"]; mdsensor_model->m_maxElevation = state["m_maxElevation"]; for (int i=0;i<3;i++){ mdsensor_model->m_boresight[i] = state["m_boresight"][i]; mdsensor_model->m_iTransL[i] = state["m_iTransL"][i]; mdsensor_model->m_iTransS[i] = state["m_iTransS"][i]; mdsensor_model->m_transX[i] = state["m_transX"][i]; mdsensor_model->m_transY[i] = state["m_transY"][i]; mdsensor_model->m_spacecraftVelocity[i] = state["m_spacecraftVelocity"][i]; mdsensor_model->m_sunPosition[i] = state["m_sunPosition"][i]; } // Having types as vectors, instead of arrays makes interoperability with // the JSON library very easy. mdsensor_model->m_currentParameterValue = state["m_currentParameterValue"].get>(); mdsensor_model->m_odtX = state["m_odtX"].get>(); mdsensor_model->m_odtY = state["m_odtY"].get>(); mdsensor_model->m_currentParameterCovariance = state["m_currentParameterCovariance"].get>(); sensor_model = mdsensor_model; return sensor_model; } // 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 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, "Sensor model support data provided is not supported by this plugin", "UsgsAstroFramePlugin::constructModelFromISD"); } // Create the empty sensorModel UsgsAstroFrameSensorModel *sensorModel = new UsgsAstroFrameSensorModel(); // Keep track of necessary keywords that are missing from the ISD. std::vector missingKeywords; sensorModel->m_startingDetectorSample = atof(imageSupportData.param("starting_detector_sample").c_str()); sensorModel->m_startingDetectorLine = atof(imageSupportData.param("starting_detector_line").c_str()); sensorModel->m_targetName = imageSupportData.param("target_name"); sensorModel->m_ifov = atof(imageSupportData.param("ifov").c_str()); sensorModel->m_instrumentID = imageSupportData.param("instrument_id"); if (imageSupportData.param("instrument_id") == "") { missingKeywords.push_back("instrument_id"); } sensorModel->m_focalLength = atof(imageSupportData.param("focal_length").c_str()); if (imageSupportData.param("focal_length") == "") { missingKeywords.push_back("focal_length"); } sensorModel->m_focalLengthEpsilon = atof(imageSupportData.param("focal_length_epsilon").c_str()); sensorModel->m_currentParameterValue[0] = atof(imageSupportData.param("x_sensor_origin").c_str()); sensorModel->m_currentParameterValue[1] = atof(imageSupportData.param("y_sensor_origin").c_str()); sensorModel->m_currentParameterValue[2] = atof(imageSupportData.param("z_sensor_origin").c_str()); if (imageSupportData.param("x_sensor_origin") == "") { missingKeywords.push_back("x_sensor_origin"); } if (imageSupportData.param("y_sensor_origin") == "") { missingKeywords.push_back("y_sensor_origin"); } if (imageSupportData.param("z_sensor_origin") == "") { missingKeywords.push_back("z_sensor_origin"); } sensorModel->m_spacecraftVelocity[0] = atof(imageSupportData.param("x_sensor_velocity").c_str()); sensorModel->m_spacecraftVelocity[1] = atof(imageSupportData.param("y_sensor_velocity").c_str()); sensorModel->m_spacecraftVelocity[2] = atof(imageSupportData.param("z_sensor_velocity").c_str()); // sensor velocity not strictly necessary? sensorModel->m_sunPosition[0] = atof(imageSupportData.param("x_sun_position").c_str()); sensorModel->m_sunPosition[1] = atof(imageSupportData.param("y_sun_position").c_str()); sensorModel->m_sunPosition[2] = atof(imageSupportData.param("z_sun_position").c_str()); // sun position is not strictly necessary, but is required for getIlluminationDirection. sensorModel->m_currentParameterValue[3] = atof(imageSupportData.param("omega").c_str()); sensorModel->m_currentParameterValue[4] = atof(imageSupportData.param("phi").c_str()); sensorModel->m_currentParameterValue[5] = atof(imageSupportData.param("kappa").c_str()); if (imageSupportData.param("omega") == "") { missingKeywords.push_back("omega"); } if (imageSupportData.param("phi") == "") { missingKeywords.push_back("phi"); } if (imageSupportData.param("kappa") == "") { missingKeywords.push_back("kappa"); } sensorModel->m_odtX[0] = atof(imageSupportData.param("odt_x", 0).c_str()); sensorModel->m_odtX[1] = atof(imageSupportData.param("odt_x", 1).c_str()); sensorModel->m_odtX[2] = atof(imageSupportData.param("odt_x", 2).c_str()); sensorModel->m_odtX[3] = atof(imageSupportData.param("odt_x", 3).c_str()); sensorModel->m_odtX[4] = atof(imageSupportData.param("odt_x", 4).c_str()); sensorModel->m_odtX[5] = atof(imageSupportData.param("odt_x", 5).c_str()); sensorModel->m_odtX[6] = atof(imageSupportData.param("odt_x", 6).c_str()); sensorModel->m_odtX[7] = atof(imageSupportData.param("odt_x", 7).c_str()); sensorModel->m_odtX[8] = atof(imageSupportData.param("odt_x", 8).c_str()); sensorModel->m_odtX[9] = atof(imageSupportData.param("odt_x", 9).c_str()); sensorModel->m_odtY[0] = atof(imageSupportData.param("odt_y", 0).c_str()); sensorModel->m_odtY[1] = atof(imageSupportData.param("odt_y", 1).c_str()); sensorModel->m_odtY[2] = atof(imageSupportData.param("odt_y", 2).c_str()); sensorModel->m_odtY[3] = atof(imageSupportData.param("odt_y", 3).c_str()); sensorModel->m_odtY[4] = atof(imageSupportData.param("odt_y", 4).c_str()); sensorModel->m_odtY[5] = atof(imageSupportData.param("odt_y", 5).c_str()); sensorModel->m_odtY[6] = atof(imageSupportData.param("odt_y", 6).c_str()); sensorModel->m_odtY[7] = atof(imageSupportData.param("odt_y", 7).c_str()); 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()); sensorModel->m_originalHalfLines = atof(imageSupportData.param("original_half_lines").c_str()); sensorModel->m_spacecraftName = imageSupportData.param("spacecraft_name"); sensorModel->m_pixelPitch = atof(imageSupportData.param("pixel_pitch").c_str()); sensorModel->m_iTransS[0] = atof(imageSupportData.param("itrans_sample", 0).c_str()); sensorModel->m_iTransS[1] = atof(imageSupportData.param("itrans_sample", 1).c_str()); sensorModel->m_iTransS[2] = atof(imageSupportData.param("itrans_sample", 2).c_str()); if (imageSupportData.param("itrans_sample", 0) == "") { missingKeywords.push_back("itrans_sample missing first element"); } else if (imageSupportData.param("itrans_sample", 1) == "") { missingKeywords.push_back("itrans_sample missing second element"); } else if (imageSupportData.param("itrans_sample", 2) == "") { missingKeywords.push_back("itrans_sample missing third element"); } sensorModel->m_ephemerisTime = atof(imageSupportData.param("ephemeris_time").c_str()); if (imageSupportData.param("ephemeris_time") == "") { missingKeywords.push_back("ephemeris_time"); } sensorModel->m_originalHalfSamples = atof(imageSupportData.param("original_half_samples").c_str()); sensorModel->m_boresight[0] = atof(imageSupportData.param("boresight", 0).c_str()); sensorModel->m_boresight[1] = atof(imageSupportData.param("boresight", 1).c_str()); sensorModel->m_boresight[2] = atof(imageSupportData.param("boresight", 2).c_str()); sensorModel->m_iTransL[0] = atof(imageSupportData.param("itrans_line", 0).c_str()); sensorModel->m_iTransL[1] = atof(imageSupportData.param("itrans_line", 1).c_str()); sensorModel->m_iTransL[2] = atof(imageSupportData.param("itrans_line", 2).c_str()); if (imageSupportData.param("itrans_line", 0) == "") { missingKeywords.push_back("itrans_line needs 3 elements"); } else if (imageSupportData.param("itrans_line", 1) == "") { missingKeywords.push_back("itrans_line needs 3 elements"); } else if (imageSupportData.param("itrans_line", 2) == "") { missingKeywords.push_back("itrans_line needs 3 elements"); } sensorModel->m_nLines = atoi(imageSupportData.param("nlines").c_str()); sensorModel->m_nSamples = atoi(imageSupportData.param("nsamples").c_str()); if (imageSupportData.param("nlines") == "") { missingKeywords.push_back("nlines"); } if (imageSupportData.param("nsamples") == "") { missingKeywords.push_back("nsamples"); } sensorModel->m_transY[0] = atof(imageSupportData.param("transy", 0).c_str()); sensorModel->m_transY[1] = atof(imageSupportData.param("transy", 1).c_str()); sensorModel->m_transY[2] = atof(imageSupportData.param("transy", 2).c_str()); if (imageSupportData.param("transy", 0) == "") { missingKeywords.push_back("transy 0"); } else if (imageSupportData.param("transy", 1) == "") { missingKeywords.push_back("transy 1"); } else if (imageSupportData.param("transy", 2) == "") { missingKeywords.push_back("transy 2"); } sensorModel->m_transX[0] = atof(imageSupportData.param("transx", 0).c_str()); sensorModel->m_transX[1] = atof(imageSupportData.param("transx", 1).c_str()); sensorModel->m_transX[2] = atof(imageSupportData.param("transx", 2).c_str()); if (imageSupportData.param("transx", 0) == "") { missingKeywords.push_back("transx 0"); } else if (imageSupportData.param("transx", 1) == "") { missingKeywords.push_back("transx 1"); } else if (imageSupportData.param("transx", 2) == "") { missingKeywords.push_back("transx"); } sensorModel->m_majorAxis = 1000 * atof(imageSupportData.param("semi_major_axis").c_str()); if (imageSupportData.param("semi_major_axis") == "") { missingKeywords.push_back("semi_major_axis"); } // Do we assume that if we do not have a semi-minor axis, then the body is a sphere? if (imageSupportData.param("semi_minor_axis") == "") { sensorModel->m_minorAxis = sensorModel->m_majorAxis; } else { sensorModel->m_minorAxis = 1000 * atof(imageSupportData.param("semi_minor_axis").c_str()); } sensorModel->m_minElevation = atof(imageSupportData.param("min_elevation").c_str()); sensorModel->m_maxElevation = atof(imageSupportData.param("max_elevation").c_str()); if (imageSupportData.param("min_elevation") == ""){ missingKeywords.push_back("min_elevation"); } if (imageSupportData.param("max_elevation") == ""){ missingKeywords.push_back("max_elevation"); } // If we are missing necessary keywords from ISD, we cannot create a valid sensor model. if (missingKeywords.size() != 0) { std::string errorMessage = "ISD is missing the necessary keywords: ["; for (int i = 0; i < (int)missingKeywords.size(); i++) { if (i == (int)missingKeywords.size() - 1) { errorMessage += missingKeywords[i] + "]"; } else { errorMessage += missingKeywords[i] + ", "; } } throw csm::Error(csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, errorMessage, "UsgsAstroFramePlugin::constructModelFromISD"); } return sensorModel; } std::string UsgsAstroFramePlugin::getModelNameFromModelState(const std::string &modelState, csm::WarningList *warnings) const { std::string name; auto state = json::parse(modelState); if(state.find("model_name") != state.end()){ name = state["model_name"]; } else{ csm::Error::ErrorType aErrorType = csm::Error::INVALID_SENSOR_MODEL_STATE; std::string aMessage = "No 'model_name' key in the model state object."; std::string aFunction = "UsgsAstroFramePlugin::getModelNameFromModelState"; csm::Error csmErr(aErrorType, aMessage, aFunction); throw(csmErr); } if (name != UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME){ csm::Error::ErrorType aErrorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED; std::string aMessage = "Sensor model not supported."; std::string aFunction = "UsgsAstroFramePlugin::getModelNameFromModelState()"; csm::Error csmErr(aErrorType, aMessage, aFunction); throw(csmErr); } return UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME; } bool UsgsAstroFramePlugin::canISDBeConvertedToModelState(const csm::Isd &imageSupportData, const std::string &modelName, csm::WarningList *warnings) const { bool convertible = true; if (modelName !=UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME){ convertible = false; } csm::Isd localImageSupportData = imageSupportData; if (imageSupportData.parameters().empty()) { localImageSupportData = loadImageSupportData(imageSupportData); } std::string value; for(auto &key : _ISD_KEYWORD){ value = localImageSupportData.param(key); if (value.empty()){ convertible = false; } } return convertible; } std::string UsgsAstroFramePlugin::convertISDToModelState(const csm::Isd &imageSupportData, const std::string &modelName, csm::WarningList *warnings) const { csm::Model* sensor_model = constructModelFromISD( imageSupportData, modelName); if (sensor_model == 0){ csm::Error::ErrorType aErrorType = csm::Error::ISD_NOT_SUPPORTED; std::string aMessage = "ISD not supported: "; std::string aFunction = "UsgsAstroFramePlugin::convertISDToModelState()"; throw csm::Error(aErrorType, aMessage, aFunction); } return sensor_model->getModelState(); }