diff --git a/.gitignore b/.gitignore index d4bc1304e71214205655228aff9b534065c45d83..c9ae0ef9fc8be859429150c5adb4048c8cf337e0 100644 --- a/.gitignore +++ b/.gitignore @@ -52,6 +52,9 @@ install/ # Unignore the documentation build !isis/src/docsys/build +# Ignore vs code +.vscode + # Created by https://www.gitignore.io/api/macos # Edit at https://www.gitignore.io/?templates=macos diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bc21c4d4962317801a6d301547059cf92ca7ce6..db9b0f42ea7815ce8ea0ca870fcaacf052da4e6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ update the Unreleased link so that it compares against the latest release tag. - Added warning to ocams2isis about the model being out of date. [#4200](https://github.com/USGS-Astrogeology/ISIS3/issues/4200) - Added documentation to lronaccal and lrowaccal to describe why there are negative DNs in I/F calibrated images. [#3860](https://github.com/USGS-Astrogeology/ISIS3/issues/3860) - Update qview MeasureTool to add an option to calculate distances using RA/DEC and update qview to show DEC/RA rather than LAT/LON in lower-right corner [#3371](https://github.com/USGS-Astrogeology/ISIS3/issues/3371) +- Added the new csminit application and CSM Library loading to the IsisPreferences file. Together these allow users to get CSM state strings from ISD files. Once CSM camera model support is added, these will be used to setup a Cube to use a CSM camera model. - Added a new application, topds4, which generates an output PDS4 XML label and a PDS4-compliant ISIS Cube from an input Cube, a PDS4 label template, and optionally additional input XML, PVL, or JSON data. The Inja templating engine is used to render the output PDS4 label from the label template. [#4246](https://github.com/USGS-Astrogeology/ISIS3/pull/4246) - Updated spiceinit so that a user can specify a shape model and use the spice web service without any errors. [#1986](https://github.com/USGS-Astrogeology/ISIS3/issues/1986) diff --git a/environment.yml b/environment.yml index bbb4391d2eb3da5c0865f51545b4ad84b7dfc045..39799387e4ec726b69d199bb67ba3eed449331f7 100644 --- a/environment.yml +++ b/environment.yml @@ -13,6 +13,7 @@ dependencies: - bzip2 - cmake >=3.15 - cspice + - csm>=3.0.3,<3.0.4 - curl - doxygen - eigen diff --git a/isis/CMakeLists.txt b/isis/CMakeLists.txt index ee5d678142a4783cc558fb16cda48de5049e2f85..f1106eb08de7506bd0cb038dab3f69bda7b32f34 100644 --- a/isis/CMakeLists.txt +++ b/isis/CMakeLists.txt @@ -268,6 +268,7 @@ find_package(Ale REQUIRED) find_package(Json REQUIRED) find_package(Bullet 2.86 REQUIRED) find_package(Cholmod 4.4.5 REQUIRED) +find_package(CSM 3.0.3.3 REQUIRED) find_package(CSPICE 65 REQUIRED) find_package(Eigen REQUIRED) find_package(Embree 2.15.0 REQUIRED) diff --git a/isis/IsisPreferences b/isis/IsisPreferences index 70e0d4d3240e85ecb54e4e2d92e094625e515298..76eeede2083b770db30ae23cceca00feff5914e8 100644 --- a/isis/IsisPreferences +++ b/isis/IsisPreferences @@ -168,6 +168,21 @@ Group = Performance GlobalThreads = Optimized EndGroup +######################################################## +# +# Indicate where ISIS should search for Community +# Sensor Model (CSM) plugins. The value of this keyword +# should be a list of paths, that contain CSM plugin +# libraries. +# +######################################################## + +Group = Plugins + CSMDirectory = ("$ISISROOT/lib/isis/csm3.0.3/", - + "$ISISROOT/csmlibs/3.0.3/", - + "$HOME/.Isis/csm3.0.3/") +EndGroup + ######################################################## # Customize the location of mission specific data # files (calibration and spice kernels). Usually this diff --git a/isis/TestPreferences b/isis/TestPreferences index 663d4de2f07f93f1028f9ef8e03de1e2e44c260e..788c8ec67e01a54e6303098a112d0361f70a4d48 100644 --- a/isis/TestPreferences +++ b/isis/TestPreferences @@ -168,6 +168,21 @@ Group = Performance GlobalThreads = 2 EndGroup +######################################################## +# +# Indicate where ISIS should search for Community +# Sensor Model (CSM) plugins. The value of this keyword +# should be a list of paths, that contain CSM plugin +# libraries. +# +######################################################## + +Group = Plugins + CSMDirectory = ("$CONDA_PREFIX/lib/isis/csm3.0.3/", - + "$CONDA_PREFIX/csmlibs/3.0.3/", - + "$HOME/.Isis/csm3.0.3/") +EndGroup + ######################################################## # Customize the location of mission specific data # files (calibration and spice kernels). Usually this diff --git a/isis/cmake/FindCSM.cmake b/isis/cmake/FindCSM.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a8ccb5cc64b95ad8d128bda2596a516ae5f4c059 --- /dev/null +++ b/isis/cmake/FindCSM.cmake @@ -0,0 +1,18 @@ +# CMake module for find_package(CSM) aka Community Sensor Model +# Finds include directory and all applicable libraries +# +# Sets the following: +# CSM_INCLUDE_DIR +# CSM_LIBRARY + +find_path(CSM_INCLUDE_DIR + NAME csm.h + PATH_SUFFIXES csm +) + +find_library(CSM_LIBRARY + NAMES csmapi +) + +message(STATUS "CSM INCLUDE: " ${CSM_INCLUDE_DIR} ) +message(STATUS "CSM LIB: " ${CSM_LIBRARY} ) diff --git a/isis/src/base/apps/csminit/Makefile b/isis/src/base/apps/csminit/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7578f0b21d038db6a5042c095cda9b34b6bb2570 --- /dev/null +++ b/isis/src/base/apps/csminit/Makefile @@ -0,0 +1,7 @@ +ifeq ($(ISISROOT), $(BLANK)) +.SILENT: +error: + echo "Please set ISISROOT"; +else + include $(ISISROOT)/make/isismake.apps +endif \ No newline at end of file diff --git a/isis/src/base/apps/csminit/csminit.cpp b/isis/src/base/apps/csminit/csminit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ac11dce07a95d2541abfe283765a6ef77f8f71f7 --- /dev/null +++ b/isis/src/base/apps/csminit/csminit.cpp @@ -0,0 +1,302 @@ +/** This is free and unencumbered software released into the public domain. + +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ +#include "csminit.h" + +#include +#include +#include + +#include "csm/csm.h" +#include "csm/GeometricModel.h" +#include "csm/Isd.h" +#include "csm/Model.h" +#include "csm/NitfIsd.h" +#include "csm/Plugin.h" + +#include "Blob.h" +#include "Camera.h" +#include "CameraFactory.h" +#include "Cube.h" +#include "IException.h" +#include "Process.h" +#include "Pvl.h" +#include "PvlGroup.h" +#include "PvlKeyword.h" +#include "StringBlob.h" + +using namespace std; + +namespace Isis { + + /** + * csminit a cube in an Application + * + * @param ui The Application UI + * @param(out) log The Pvl that attempted models will be logged to + */ + void csminit(UserInterface &ui, Pvl *log) { + // We are not processing the image data, so this process object is just for + // managing the Cube in memory and adding history + Process p; + // Get the cube here so that we check early if it doesn't exist + Cube *cube = p.SetInputCube(ui.GetFileName("FROM"), ui.GetInputAttribute("FROM"), ReadWrite); + + // We have to call this to get the plugin list loaded right now + try { + Camera *cam = CameraFactory::Create(*cube); + delete cam; + } + catch(...) { + // Noop + } + + QString isdFilePath = ui.GetFileName("ISD"); + + QList possibleModels; + for (const csm::Plugin * plugin : csm::Plugin::getList()) { + QString pluginName = QString::fromStdString(plugin->getPluginName()); + if (ui.WasEntered("PLUGINNAME") && pluginName != ui.GetString("PLUGINNAME")) { + continue; + } + + for (size_t modelIndex = 0; modelIndex < plugin->getNumModels(); modelIndex++) { + QString modelName = QString::fromStdString(plugin->getModelName(modelIndex)); + if (ui.WasEntered("MODELNAME") && modelName != ui.GetString("MODELNAME")) { + continue; + } + + csm::Isd fileIsd(isdFilePath.toStdString()); + if (plugin->canModelBeConstructedFromISD(fileIsd, modelName.toStdString())) { + QStringList modelSpec = {pluginName, modelName, QString::fromStdString(fileIsd.format())}; + possibleModels.append(modelSpec); + continue; // If the file ISD works, don't check the others + } + + csm::Nitf21Isd nitf21Isd(isdFilePath.toStdString()); + if (plugin->canModelBeConstructedFromISD(nitf21Isd, modelName.toStdString())) { + QStringList modelSpec = {pluginName, modelName, QString::fromStdString(nitf21Isd.format())}; + possibleModels.append(modelSpec); + continue; // If the NITF 2.1 ISD works, don't check the others + } + } + } + + if (possibleModels.size() > 1) { + QString message = "Multiple models can be created from the ISD [" + isdFilePath + "]. " + "Re-run with the PLUGINNAME and MODELNAME parameters. " + "Possible plugin & model names:\n"; + for (const QStringList &modelSpec : possibleModels) { + message += "Plugin [" + modelSpec[0] + "], Model [" + modelSpec[1] + "]\n"; + } + throw IException(IException::User, message, _FILEINFO_); + } + + if (possibleModels.empty()) { + QString message = "No loaded model could be created from the ISD [" + isdFilePath + "]." + "Loaded plugin & model names:\n"; + for (const csm::Plugin * plugin : csm::Plugin::getList()) { + QString pluginName = QString::fromStdString(plugin->getPluginName()); + for (size_t modelIndex = 0; modelIndex < plugin->getNumModels(); modelIndex++) { + QString modelName = QString::fromStdString(plugin->getModelName(modelIndex)); + message += "Plugin [" + pluginName + "], Model [" + modelName + "]\n"; + } + } + throw IException(IException::User, message, _FILEINFO_); + } + + // If we are here, then we have exactly 1 model + QStringList modelSpec = possibleModels.front(); + if (modelSpec.size() != 3) { + QString message = "Model specification [" + modelSpec.join(" ") + "] has [" + modelSpec.size() + "] elements " + "when it should have 3 elements."; + throw IException(IException::Programmer, message, _FILEINFO_); + } + const csm::Plugin *plugin = csm::Plugin::findPlugin(modelSpec[0].toStdString()); + csm::Model *model; + csm::Isd fileIsd(isdFilePath.toStdString()); + csm::Nitf21Isd nitf21Isd(isdFilePath.toStdString()); + if (modelSpec[2] == QString::fromStdString(fileIsd.format())) { + model = plugin->constructModelFromISD(fileIsd, modelSpec[1].toStdString()); + } + else if (modelSpec[2] == QString::fromStdString(nitf21Isd.format())) { + model = plugin->constructModelFromISD(nitf21Isd, modelSpec[1].toStdString()); + } + else { + QString message = "Invalid ISD format specifications [" + modelSpec[2] + "]."; + throw IException(IException::Programmer, message, _FILEINFO_); + } + + string modelState = model->getModelState(); + + // Add the TargetName to the instrument group, if specified: + if (ui.WasEntered("TARGETNAME")) { + if (!cube->hasGroup("Instrument")) { + cube->putGroup(PvlGroup("Instrument")); + } + PvlGroup &instrumentGroup = cube->group("Instrument"); + instrumentGroup.addKeyword(PvlKeyword("TargetName", ui.GetString("TARGETNAME")), Pvl::Replace); + } + + // Populate the CsmInfo group with useful information + cube->deleteGroup("CsmInfo"); + PvlGroup infoGroup("CsmInfo"); + infoGroup += PvlKeyword("CSMPlatformID", + QString::fromStdString(model->getPlatformIdentifier())); + infoGroup += PvlKeyword("CSMInstrumentId", + QString::fromStdString(model->getSensorIdentifier())); + infoGroup += PvlKeyword("ReferenceTime", + QString::fromStdString(model->getReferenceDateAndTime())); + csm::GeometricModel *modelWithParams = dynamic_cast(model); + if (modelWithParams) { + PvlKeyword paramNames("ModelParameterNames"); + PvlKeyword paramUnits("ModelParameterUnits"); + PvlKeyword paramTypes("ModelParameterTypes"); + for (const csm::GeometricModel::Parameter ¶m : modelWithParams->getParameters()) { + paramNames += QString::fromStdString(param.name); + paramUnits += QString::fromStdString(param.units); + switch (param.type) { + case csm::param::NONE: + paramTypes += "NONE"; + break; + + case csm::param::FICTITIOUS: + paramTypes += "FICTITIOUS"; + break; + + case csm::param::REAL: + paramTypes += "REAL"; + break; + + case csm::param::FIXED: + paramTypes += "FIXED"; + break; + + default: + paramTypes += "UNKNOWN"; + break; + } + } + + infoGroup += paramNames; + infoGroup += paramUnits; + infoGroup += paramTypes; + } + cube->putGroup(infoGroup); + + // Update existing Kernels Group or create new one and add shapemodel if provided + if (!cube->hasGroup("Kernels")) { + cube->putGroup(PvlGroup("Kernels")); + } + PvlGroup &kernelsGroup = cube->group("Kernels"); + + if (ui.WasEntered("SHAPEMODEL")) { + // TODO validate the shapemodel + kernelsGroup.addKeyword(PvlKeyword("ShapeModel", ui.GetString("SHAPEMODEL")), Pvl::Replace); + } + else { + kernelsGroup.addKeyword(PvlKeyword("ShapeModel", "Ellipsoid"), Pvl::Replace); + } + + // Get rid of keywords from spiceinit + if (kernelsGroup.hasKeyword("LeapSecond")) { + kernelsGroup.deleteKeyword("LeapSecond"); + } + if (kernelsGroup.hasKeyword("TargetAttitudeShape")) { + kernelsGroup.deleteKeyword("TargetAttitudeShape"); + } + if (kernelsGroup.hasKeyword("TargetPosition")) { + kernelsGroup.deleteKeyword("TargetPosition"); + } + if (kernelsGroup.hasKeyword("InstrumentPointing")) { + kernelsGroup.deleteKeyword("InstrumentPointing"); + } + if (kernelsGroup.hasKeyword("InstrumentPointingQuality")) { + kernelsGroup.deleteKeyword("InstrumentPointingQuality"); + } + if (kernelsGroup.hasKeyword("Instrument")) { + kernelsGroup.deleteKeyword("Instrument"); + } + if (kernelsGroup.hasKeyword("SpacecraftClock")) { + kernelsGroup.deleteKeyword("SpacecraftClock"); + } + if (kernelsGroup.hasKeyword("InstrumentPositionQuality")) { + kernelsGroup.deleteKeyword("InstrumentPositionQuality"); + } + if (kernelsGroup.hasKeyword("InstrumentPosition")) { + kernelsGroup.deleteKeyword("InstrumentPosition"); + } + if (kernelsGroup.hasKeyword("InstrumentAddendum")) { + kernelsGroup.deleteKeyword("InstrumentAddendum"); + } + if (kernelsGroup.hasKeyword("EXTRA")) { + kernelsGroup.deleteKeyword("EXTRA"); + } + if (kernelsGroup.hasKeyword("Source")) { + kernelsGroup.deleteKeyword("Source"); + } + if (kernelsGroup.hasKeyword("SpacecraftPointing")) { + kernelsGroup.deleteKeyword("SpacecraftPointing"); + } + if (kernelsGroup.hasKeyword("SpacecraftPosition")) { + kernelsGroup.deleteKeyword("SpacecraftPosition"); + } + if (kernelsGroup.hasKeyword("CameraVersion")) { + kernelsGroup.deleteKeyword("CameraVersion"); + } + if (kernelsGroup.hasKeyword("ElevationModel")) { + kernelsGroup.deleteKeyword("ElevationModel"); + } + if (kernelsGroup.hasKeyword("Frame")) { + kernelsGroup.deleteKeyword("Frame"); + } + if (kernelsGroup.hasKeyword("StartPadding")) { + kernelsGroup.deleteKeyword("StartPadding"); + } + if (kernelsGroup.hasKeyword("EndPadding")) { + kernelsGroup.deleteKeyword("EndPadding"); + } + if (kernelsGroup.hasKeyword("RayTraceEngine")) { + kernelsGroup.deleteKeyword("RayTraceEngine"); + } + if (kernelsGroup.hasKeyword("OnError")) { + kernelsGroup.deleteKeyword("OnError"); + } + if (kernelsGroup.hasKeyword("Tolerance")) { + kernelsGroup.deleteKeyword("Tolerance"); + } + + // Remove tables from spiceinit + cube->deleteBlob("Table", "InstrumentPointing"); + cube->deleteBlob("Table", "InstrumentPosition"); + cube->deleteBlob("Table", "BodyRotation"); + cube->deleteBlob("Table", "SunPosition"); + cube->deleteBlob("Table", "CameraStatistics"); + cube->deleteBlob("Polygon", "Footprint"); + + if (cube->label()->hasObject("NaifKeywords")) { + cube->label()->deleteObject("NaifKeywords"); + } + + cube->deleteBlob("String", "CSMState"); + + // Create our CSM State blob as a string + // Add the CSM string to the Blob. + StringBlob csmStateBlob(modelState, "CSMState"); + PvlObject &blobLabel = csmStateBlob.Label(); + blobLabel += PvlKeyword("ModelName", QString::fromStdString(model->getModelName())); + blobLabel += PvlKeyword("PluginName", QString::fromStdString(plugin->getPluginName())); + + // Write CSM State blob to cube + cube->write(csmStateBlob); + + // TODO attempt to get the CSM Model from the cube + + p.WriteHistory(*cube); + } + +} diff --git a/isis/src/base/apps/csminit/csminit.h b/isis/src/base/apps/csminit/csminit.h new file mode 100644 index 0000000000000000000000000000000000000000..dc861d7965e7788a95b13d0c8b30e14740ba3b9c --- /dev/null +++ b/isis/src/base/apps/csminit/csminit.h @@ -0,0 +1,18 @@ +#ifndef csminit_h +#define csminit_h +/** This is free and unencumbered software released into the public domain. + +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#include "Pvl.h" +#include "UserInterface.h" + +namespace Isis { + extern void csminit(UserInterface &ui, Pvl *log=nullptr); +} + +#endif diff --git a/isis/src/base/apps/csminit/csminit.xml b/isis/src/base/apps/csminit/csminit.xml new file mode 100644 index 0000000000000000000000000000000000000000..037ae3155b8bebbb80a552d8c099b704eda65bfd --- /dev/null +++ b/isis/src/base/apps/csminit/csminit.xml @@ -0,0 +1,129 @@ + + + + + Attach a CSM model state to a cube. + + + +

+ This program takes Instrument Support Data (ISD) and attempts to create a + valid Community Sensor Model (CSM) state string using models from the + loaded CSM Libraries. If a single state string can be created, it is + attached to the cube in the CSMState BLOB for later use. If there are + multiple models that create valid state strings, then this program will + error and return a list of possible models. The user will then have to + re-run this program using the PLUGINNAME and/or MODELNAME parameters to + select a specific model. +

+

+ Information about the CSM model will also be added to the CsmInfo group on + the cube label. These are just for users to reference and are not used + programmatically. +

+

+ This program can be run on any Cube file, but if the Cube has spice data + from spiceinit then it will be removed so that there is no ambiguity + between the ISIS and CSM model. +

+

+ See the Plugins group in the IsisPreferences file for information about + how to load CSM Libraries for use with this and other ISIS applications. +

+
+ + + Cameras + + + + + Original Version + + + + + + + cube + input + + The input cube that the state string will be attached to. + + + The input cube that the state string will be attached to. Any kernels + specified in the kernels group and any attached SPICE tables will be + removed when the model state is attached. + + *.cub + + + + filename + input + + The Instrument Support Data (ISD) file that will be used. + + + The Instrument Support Data (ISD) file that will be used to create the + model state string. The ISD can be just a filename ISD or a NITF2.1 ISD. + + + + + + + string + none + + The name of the body observed by the image. + + + The name of the body observed by the image. If not entered, then the + target already listed on the label will be used. + + + + + filename + none + + The shapemodel to represent the surface of the observed body. + + + The shapemodel to represent the surface of the observed body. If none + is entered, then a bi-axial ellipsoid is used. The radii are defined + by the CSM model. + + + + + + + string + none + + The name of the plugin to use. + + + The name of the plugin to use. The loaded CSM Libraries will be + searched for a plugin that has this name. If no such plugin is found + an error will be returned. + + + + + string + none + + The name of the model to use. + + + The name of the model to use. The loaded CSM Libraries will be + searched for a model that has this name. If no such model is found an + error will be returned. + + + + +
diff --git a/isis/src/base/apps/csminit/main.cpp b/isis/src/base/apps/csminit/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f113ef57ab8f2695897bec0d2f8b494076169427 --- /dev/null +++ b/isis/src/base/apps/csminit/main.cpp @@ -0,0 +1,35 @@ +/** This is free and unencumbered software released into the public domain. + +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ +#include "Isis.h" + +#include "csminit.h" + +#include "Application.h" +#include "IException.h" +#include "Pvl.h" + +using namespace std; +using namespace Isis; + +void IsisMain() { + UserInterface &ui = Application::GetUserInterface(); + Pvl appLog; + try { + csminit(ui, &appLog); + } + catch (...) { + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); + } + throw; + } + + for (auto grpIt = appLog.beginGroup(); grpIt!= appLog.endGroup(); grpIt++) { + Application::Log(*grpIt); + } +} diff --git a/isis/src/base/apps/spiceinit/spiceinit.cpp b/isis/src/base/apps/spiceinit/spiceinit.cpp index 1654607e07b241a877af186564c48e5370a9ea27..c1a8f6dd28a43d3b676cdce505c96ce7b3dc82a3 100644 --- a/isis/src/base/apps/spiceinit/spiceinit.cpp +++ b/isis/src/base/apps/spiceinit/spiceinit.cpp @@ -255,11 +255,14 @@ namespace Isis { realCkKernel, fk, ik, sclk, spk, iak, dem, exk); } - if (!kernelSuccess) + if (!kernelSuccess) { throw IException(IException::Unknown, "Unable to initialize camera model", _FILEINFO_); + } } + icube->deleteGroup("CsmInfo"); + icube->deleteBlob("String","CSMState"); p.WriteHistory(*icube); p.EndProcess(); } diff --git a/isis/src/base/apps/spiceinit/spiceinit.xml b/isis/src/base/apps/spiceinit/spiceinit.xml index f06b9ef8b77670a824e682440c96f05951961a6c..37f749fbb36cfb7d4dd5f3360e4f4924113279c1 100644 --- a/isis/src/base/apps/spiceinit/spiceinit.xml +++ b/isis/src/base/apps/spiceinit/spiceinit.xml @@ -295,6 +295,9 @@ derived from bundle adjustment can be used alongside CKs derived from bundle adjustment. Fixes #3669. + + Modified to remove state string and model information from the csminit application. + Updated shape model keyword in the kernels group when shape=user and web=true with the user specified model. Fixes #1986 diff --git a/isis/src/base/objs/CameraFactory/CameraFactory.cpp b/isis/src/base/objs/CameraFactory/CameraFactory.cpp index a40bf25881e27ace1f31986f3f2bac9618d20f01..3b946152e70a35869c05ee18f40cf8e4d6e54552 100644 --- a/isis/src/base/objs/CameraFactory/CameraFactory.cpp +++ b/isis/src/base/objs/CameraFactory/CameraFactory.cpp @@ -20,13 +20,22 @@ * http://www.usgs.gov/privacy.html. */ + +#include +#include +#include +#include +#include + #include "CameraFactory.h" #include "Camera.h" -#include "Plugin.h" -#include "IException.h" #include "FileName.h" +#include "IException.h" +#include "Plugin.h" +#include "Preference.h" +using namespace csm; using namespace std; namespace Isis { @@ -102,9 +111,12 @@ namespace Isis { /** - * Reads the appropriate plugin file for the camera + * Reads the appropriate plugin file for the ISIS cameras, and scans the + * directories specified in IsisPreferences for CSM cameras. */ void CameraFactory::initPlugin() { + + // Handle the ISIS camera plugins if (m_cameraPlugin.fileName() == "") { FileName localFile("Camera.plugin"); if (localFile.fileExists()) @@ -114,6 +126,22 @@ namespace Isis { if (systemFile.fileExists()) m_cameraPlugin.read(systemFile.expanded()); } + + // Find the CSM plugins by searching the directories identified in the Preferences. + // Load the found libraries. This causes the static instance(s) to be constructed, + // and thus registering the model with the csm Plugin class. + Preference &p = Preference::Preferences(); + PvlGroup &grp = p.findGroup("Plugins", Isis::Pvl::Traverse); + for (int i = 0; i +#include "StringBlob.h" +#include "Application.h" + +using namespace std; +namespace Isis { + /** + * Constructor for creating a string blob with no arguments + */ + StringBlob::StringBlob() : Isis::Blob("IsisCube", "String") { + m_string = ""; + } + + /** + * Constructor for creating a string blob with a file to + * read labels from. + * + * @param file File to read labels from + */ + StringBlob::StringBlob(const QString &file) : + Isis::Blob("IsisCube", "String") { + Blob::Read(file); + } + + /** + * Constructor for creating a string blob with a standard string + * + * @param string String to read/write from the cube. + */ + StringBlob::StringBlob(std::string str, QString name) : Isis::Blob(name, "String") { + m_string = str; + } + + // Destructor + StringBlob::~StringBlob() { + } + + /** + * Prepare to write string to output cube + */ + void StringBlob::WriteInit() { + int bytes = m_string.size(); + + char *temp = p_buffer; + p_buffer = new char[p_nbytes+bytes]; + if (temp != NULL) memcpy(p_buffer, temp, p_nbytes); + const char *ptr = m_string.c_str(); + memcpy(&p_buffer[p_nbytes], (void *)ptr, bytes); + p_nbytes += bytes; + + if (temp != NULL) delete [] temp; + } + + /** + * Read binary data from an input stream into the string. + * + * @param stream The input stream to read from. + * + * @throws IException::Io - Error reading data from stream + */ + void StringBlob::ReadData(std::istream &stream) { + // Read the binary data + if (p_buffer != NULL) delete [] p_buffer; + p_buffer = new char[p_nbytes]; + + streampos sbyte = p_startByte - 1; + stream.seekg(sbyte, std::ios::beg); + if (!stream.good()) { + QString msg = "Error preparing to read data from " + p_type + + " [" + p_blobName + "]"; + throw IException(IException::Io, msg, _FILEINFO_); + } + + stream.read(p_buffer, p_nbytes); + if (!stream.good()) { + QString msg = "Error reading data from " + p_type + " [" + p_blobName + "]"; + throw IException(IException::Io, msg, _FILEINFO_); + } + + m_string = std::string(p_buffer, p_nbytes); + } +} diff --git a/isis/src/base/objs/StringBlob/StringBlob.h b/isis/src/base/objs/StringBlob/StringBlob.h new file mode 100644 index 0000000000000000000000000000000000000000..ab0723b4b6f8d42d63c5ab09c5bee88740de015e --- /dev/null +++ b/isis/src/base/objs/StringBlob/StringBlob.h @@ -0,0 +1,50 @@ +#ifndef StringBlob_h +#define StringBlob_h + +/** This is free and unencumbered software released into the public domain. + +The authors of ISIS do not claim copyright on the contents of this file. +For more details about the LICENSE terms and the AUTHORS, you will +find files of those names at the top level of this repository. **/ + +/* SPDX-License-Identifier: CC0-1.0 */ + +#include + +#include "Blob.h" + +namespace Isis { + /** + * @brief Read and store std::strings on the cube. + * + * + * @ingroup LowLevelCubeIO + * + * @author 2020-11-19 Kristin Berry - Original Version + * + * @internal + * @history 2020-11-19 Kristin Berry - Original Version + */ + class StringBlob : public Isis::Blob { + public: + StringBlob(); + StringBlob(const QString &file); + StringBlob(std::string str, QString name); + ~StringBlob(); + + std::string string() { + return m_string; + } + + protected: + // prepare data for writing + void WriteInit(); + void ReadData(std::istream &stream); + + private: + std::string m_string; + }; +}; + +#endif + diff --git a/isis/tests/AlternativeTestCsmModel.cpp b/isis/tests/AlternativeTestCsmModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a9c6d4f29dc4e309750f8bad9ee9dd16495f997b --- /dev/null +++ b/isis/tests/AlternativeTestCsmModel.cpp @@ -0,0 +1,492 @@ +#include "AlternativeTestCsmModel.h" +#include +#include +#include +using json = nlohmann::json; + + +// Sensor model Name +const std::string AlternativeTestCsmModel::SENSOR_MODEL_NAME = "AlternativeTestCsmModel"; + +// Sensor model Parameter names +const std::vector AlternativeTestCsmModel::PARAM_NAMES = { + "test_param_one", + "test_param_two", + "test_param_three", + "test_param_four" +}; + +// Sensor model Parameter units +const std::vector AlternativeTestCsmModel::PARAM_UNITS = { + "m", + "rad", + "K", + "unitless" +}; + +// Sensor model Parameter Types +const std::vector AlternativeTestCsmModel::PARAM_TYPES = { + csm::param::FICTITIOUS, + csm::param::REAL, + csm::param::FIXED, + csm::param::NONE +}; + +// Sensor model Parameter sharing criteria +const std::vector AlternativeTestCsmModel::PARAM_SHARING_CRITERIA = { + csm::SharingCriteria(), + csm::SharingCriteria(), + csm::SharingCriteria(), + csm::SharingCriteria() +}; + + +/** + * Constructor. Resizes the parameter values list based on the number of PARAM_NAMES. + */ +AlternativeTestCsmModel::AlternativeTestCsmModel() { + m_param_values.resize(AlternativeTestCsmModel::PARAM_NAMES.size(), 0.0); +}; + + +/** + * Default destructor + */ +AlternativeTestCsmModel::~AlternativeTestCsmModel() { +}; + + +/** + * Returns the sensor model family. + * + * @return std::string Sensor model family + */ +std::string AlternativeTestCsmModel::getFamily() const { + return "AlternativeTestCsmModelFamily"; +} + + +/** + * Returns the version of the sensor model + * + * @return csm::Version sensor model version + */ +csm::Version AlternativeTestCsmModel::getVersion() const { + return csm::Version(1,0,0); +} + + +/** + * Returns the name of the sensor model. + * + * @return std::string sensor model name + */ +std::string AlternativeTestCsmModel::getModelName() const { + return AlternativeTestCsmModel::SENSOR_MODEL_NAME; +} + + +/** + * Returns the pedigree of the sensor model. + * + * @return std::string sensor model pedigree + */ +std::string AlternativeTestCsmModel::getPedigree() const { + return "AlternativeTestCsmModelPedigree"; +} + + +/** + * Returns the image identifier. + * + * @return std::string image identifier + */ +std::string AlternativeTestCsmModel::getImageIdentifier() const { + return "AlternativeTestCsmModelImageIdentifier"; +} + + +/** + * Does nothing. Empty implementation for test. + * + * @param imageId image identifier + * @param warnings CSM warnings list + */ +void AlternativeTestCsmModel::setImageIdentifier(const std::string& imageId, + csm::WarningList* warnings) { + // do nothing for test +} + + +/** + * Returns the sensor identifier for the sensor model. + * + * @return std::string sensor identifier + */ +std::string AlternativeTestCsmModel::getSensorIdentifier() const { + return "AlternativeTestCsmModelSensorIdentifier"; +} + + +/** + * Returns the platform identifier for the sensor model. + * + * @return std::string platform identifier + */ +std::string AlternativeTestCsmModel::getPlatformIdentifier() const { + return "AlternativeTestCsmModel_PlatformIdentifier"; +} + + +/** + * Returns the collection identifier for the sensor model. + * + * @return std::string collection identifier + */ +std::string AlternativeTestCsmModel::getCollectionIdentifier() const { + return "AlternativeTestCsmModel_CollectionIdentifier"; +} + + +/** + * Returns the trajectory identifier for the sensor model. + * + * @return std::string trajectory identifier + */ +std::string AlternativeTestCsmModel::getTrajectoryIdentifier() const { + return "AlternativeTestCsmModel_TrajectoryIdentifier"; +} + + +/** + * Reeturns the sensor type for the sensor model. + * + * @return std::string sensor type + */ +std::string AlternativeTestCsmModel::getSensorType() const { + return "AlternativeTestCsmModel_SensorType"; +} + + +/** + * Returns the sensor mode for the sensor model + * + * @return std::string sensor mode + */ +std::string AlternativeTestCsmModel::getSensorMode() const { + return "AlternativeTestCsmModel_SensorMode"; +} + + +/** + * Returns the reference date and time for the sensor model + * + * @return std::string reference date and time + */ +std::string AlternativeTestCsmModel::getReferenceDateAndTime() const { + return "AlternativeTestCsmModel_ReferenceDateTime"; +} + + +/** + * Returns the current model state for the sensor model. + * + * @return std::string model state + */ +std::string AlternativeTestCsmModel::getModelState() const { + json state; + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + state[AlternativeTestCsmModel::PARAM_NAMES[param_index]] = m_param_values[param_index]; + } + return AlternativeTestCsmModel::SENSOR_MODEL_NAME + "\n" + state.dump(); +} + + +/** + * Uses the supplied sensor model state to set the steat of the current sensor model. + * + * @param argState the model state + */ +void AlternativeTestCsmModel::replaceModelState(const std::string& argState) { + // Get the JSON substring + json state = json::parse(argState.substr(argState.find("\n") + 1)); + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + m_param_values[param_index] = state.at(AlternativeTestCsmModel::PARAM_NAMES[param_index]); + } +} + + +/** + * Constructs and returns a sensor model state from an ISD. + * + * @param isd instrument support data + * + * @return std::string sensor model state + */ +std::string AlternativeTestCsmModel::constructStateFromIsd(const csm::Isd isd){ + std::string filename = isd.filename(); + std::ifstream isdFile(filename); + + if (isdFile.fail()) { + std::cout << "Could not open file: " << filename << std::endl; + } + + json parsedIsd; + isdFile >> parsedIsd; + + json state; + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + state[AlternativeTestCsmModel::PARAM_NAMES[param_index]] = parsedIsd.at(AlternativeTestCsmModel::PARAM_NAMES[param_index]); + } + return AlternativeTestCsmModel::SENSOR_MODEL_NAME + "\n" + state.dump(); +} + + +/** + * Returns a default reference point. + * + * @return csm::EcefCoord reference point + */ +csm::EcefCoord AlternativeTestCsmModel::getReferencePoint() const { + return csm::EcefCoord(0.0, 0.0, 0.0); +} + + +/** + * Does nothing. Minimal implementation for test. + * + * @param groundPt the ground point + */ +void AlternativeTestCsmModel::setReferencePoint(const csm::EcefCoord& groundPt) { + // do nothing for test +} + + +/** + * Returns the number of sensor model parameters + * + * @return int number of parameters + */ +int AlternativeTestCsmModel::getNumParameters() const { + return m_param_values.size(); +} + + +/** + * Returns the semsor model parameter name at the provided index + * + * @param index parameter index + * + * @return std::string parameter name + */ +std::string AlternativeTestCsmModel::getParameterName(int index) const { + return AlternativeTestCsmModel::PARAM_NAMES[index]; +} + + +/** + * Returns the sensor model parameter units at the provided index + * + * @param index parameter unit index + * + * @return std::string parameter units + */ +std::string AlternativeTestCsmModel::getParameterUnits(int index) const { + return AlternativeTestCsmModel::PARAM_UNITS[index]; +} + + +/** + * True if the sensor model has sharable parameters. + * + * @return bool Always returns false + */ +bool AlternativeTestCsmModel::hasShareableParameters() const { + return false; +} + + +/** + * True if the sensor model parameter at the provided index is sharable. + * + * @param index Parameter index + * + * @return bool Always returns false + */ +bool AlternativeTestCsmModel::isParameterShareable(int index) const { + return false; +} + + +/** + * Returns the sharing criteria for the sensor model parameter at the provided index + * + * @param index Parameter index + * + * @return csm::SharingCriteria CSM sharing criteria for the parameter + */ +csm::SharingCriteria AlternativeTestCsmModel::getParameterSharingCriteria(int index) const { + return AlternativeTestCsmModel::PARAM_SHARING_CRITERIA[index]; +} + + +/** + * Returns the sensor model parameter value at the provided index. + * + * @param index Parameter index + * + * @return double Value at provided index + */ +double AlternativeTestCsmModel::getParameterValue(int index) const { + return m_param_values[index]; +} + + +/** + * Set the sensor model parameter at the provided index to the provided + * value. + * + * @param index Parameter index + * @param value Value to set the parameter to + */ +void AlternativeTestCsmModel::setParameterValue(int index, double value) { + m_param_values[index] = value; +} + + +/** + * Returns the type of the sensor model parameter at the provided index. + * + * @param index Parameter index + * + * @return csm::param::Type Type of parameter + */ +csm::param::Type AlternativeTestCsmModel::getParameterType(int index) const { + return AlternativeTestCsmModel::PARAM_TYPES[index]; +} + + +/** + * Does nothing. Minimal implementation for testing + * + * @param index Parameter index + * @param pType Parameter type + */ +void AlternativeTestCsmModel::setParameterType(int index, csm::param::Type pType) { + // do nothing for test +} + + +/** + * Returns the covariance between the two sensor model parameters at the provided indicies. + * Defaults to identity covariance matrix for testing. + * + * @param index1 First parameter index + * @param index2 Second parameter index + * + * @return double Parameter covariance + */ +double AlternativeTestCsmModel::getParameterCovariance(int index1, + int index2) const { + // default to identity covariance matrix + if (index1 == index2) { + return 1.0; + } + return 0.0; +} + +/** + * Does nothing. Minimal implementation for testing. + * + * @param index1 First parameter index + * @param index2 Second parameter index + * @param covariance Covariance between the two parameters + */ +void AlternativeTestCsmModel::setParameterCovariance(int index1, + int index2, + double covariance) { + // do nothing for test +} + + +/** + * Returns the number of geometric correction switches. + * + * @return int Number of geometric correction switches. + */ +int AlternativeTestCsmModel::getNumGeometricCorrectionSwitches() const { + return 0; +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * + * @return std::string Geometric correction + */ +std::string AlternativeTestCsmModel::getGeometricCorrectionName(int index) const { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "AlternativeTestCsmModel::getGeometricCorrectionName"); +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * @param value Value to set + * @param pType Parameter type + */ +void AlternativeTestCsmModel::setGeometricCorrectionSwitch(int index, + bool value, + csm::param::Type pType) { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "AlternativeTestCsmModel::setGeometricCorrectionSwitch"); +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * + * @return bool If the geometric correction switch can be accessed. + */ +bool AlternativeTestCsmModel::getGeometricCorrectionSwitch(int index) const { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "AlternativeTestCsmModel::getGeometricCorrectionSwitch"); +} + + +/** + * Returns the cross covariance matrix. + * + * @param comparisonModel The geometric model to compare with. + * @param pSet Set of parameters to use + * @param otherModels Not used. + * + * @return std::vector covariance matrix + */ +std::vector AlternativeTestCsmModel::getCrossCovarianceMatrix( + const csm::GeometricModel& comparisonModel, + csm::param::Set pSet, + const csm::GeometricModel::GeometricModelList& otherModels) const { + const std::vector& rowIndices = getParameterSetIndices(pSet); + size_t numRows = rowIndices.size(); + const std::vector& colIndices = comparisonModel.getParameterSetIndices(pSet); + size_t numCols = colIndices.size(); + std::vector covariance(numRows * numCols, 0.0); + + if (&comparisonModel == this) { + for (size_t rowIndex = 0; rowIndex < numRows; numRows++) { + for (size_t colIndex = 0; colIndex < numCols; colIndex++) { + covariance[rowIndex * numCols + colIndex] = getParameterCovariance(rowIndices[rowIndex], + colIndices[colIndex]); + } + } + } + + return covariance; +} diff --git a/isis/tests/AlternativeTestCsmModel.h b/isis/tests/AlternativeTestCsmModel.h new file mode 100644 index 0000000000000000000000000000000000000000..d11e53258607cb70c4af224ce46f5dd84dba2dfe --- /dev/null +++ b/isis/tests/AlternativeTestCsmModel.h @@ -0,0 +1,80 @@ +#ifndef AlternativeTestCsmModel_h +#define AlternativeTestCsmModel_h + +#include + +#include "csm/Plugin.h" +#include "csm/GeometricModel.h" +#include "csm/Version.h" + +/** + * An alternative Test CSM (Community Sensor Model) Sensor Model used to test + * CSM sensor model support in ISIS. This can be used to help test situations + * where multiple potential sensor models can be constructed, or to make sure + * that specific sensor model requirements are being met. + * + * @author 2020-12-08 Kristin Berry + */ +class AlternativeTestCsmModel : public csm::GeometricModel { + public: + // Static variables that describe the model + static const std::string SENSOR_MODEL_NAME; + static const std::vector PARAM_NAMES; + static const std::vector PARAM_UNITS; + static const std::vector PARAM_TYPES; + static const std::vector PARAM_SHARING_CRITERIA; + + AlternativeTestCsmModel(); + ~AlternativeTestCsmModel(); + // csm::Model methods + std::string getFamily() const; + csm::Version getVersion() const; + std::string getModelName() const; + std::string getPedigree() const; + std::string getImageIdentifier() const; + void setImageIdentifier(const std::string& imageId, + csm::WarningList* warnings = NULL); + std::string getSensorIdentifier() const; + std::string getPlatformIdentifier() const; + std::string getCollectionIdentifier() const; + std::string getTrajectoryIdentifier() const; + std::string getSensorType() const; + std::string getSensorMode() const; + std::string getReferenceDateAndTime() const; + std::string getModelState() const; + void replaceModelState(const std::string& argState); + std::string constructStateFromIsd(const csm::Isd stringIsd); + // csm::GeometricModel methods + csm::EcefCoord getReferencePoint() const; + void setReferencePoint(const csm::EcefCoord& groundPt); + int getNumParameters() const; + std::string getParameterName(int index) const; + std::string getParameterUnits(int index) const; + bool hasShareableParameters() const; + bool isParameterShareable(int index) const; + csm::SharingCriteria getParameterSharingCriteria(int index) const; + double getParameterValue(int index) const; + void setParameterValue(int index, double value); + csm::param::Type getParameterType(int index) const; + void setParameterType(int index, csm::param::Type pType); + double getParameterCovariance(int index1, + int index2) const; + void setParameterCovariance(int index1, + int index2, + double covariance); + int getNumGeometricCorrectionSwitches() const; + std::string getGeometricCorrectionName(int index) const; + void setGeometricCorrectionSwitch(int index, + bool value, + csm::param::Type pType); + bool getGeometricCorrectionSwitch(int index) const; + std::vector getCrossCovarianceMatrix( + const csm::GeometricModel& comparisonModel, + csm::param::Set pSet = csm::param::VALID, + const csm::GeometricModel::GeometricModelList& otherModels = GeometricModel::GeometricModelList()) const; + + private: + std::string m_modelState; + std::vector m_param_values; +}; +#endif diff --git a/isis/tests/FunctionalTestsCsminit.cpp b/isis/tests/FunctionalTestsCsminit.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d256f91574145fef421d96b4ddda9855d5beaf78 --- /dev/null +++ b/isis/tests/FunctionalTestsCsminit.cpp @@ -0,0 +1,352 @@ +#include "csminit.h" + +#include +#include + +#include "AlternativeTestCsmModel.h" +#include "TestCsmPlugin.h" +#include "Fixtures.h" +#include "TestUtilities.h" +#include "TestCsmModel.h" +#include "StringBlob.h" +#include "FileName.h" + +#include +using json = nlohmann::json; + +#include "gmock/gmock.h" + +using namespace Isis; + +static QString APP_XML = FileName("$ISISROOT/bin/xml/csminit.xml").expanded(); + +class CSMPluginFixture : public TempTestingFiles { + protected: + const csm::Plugin *plugin; + Cube *testCube; + Pvl label; + QString isdPath; + QString altIsdPath; + QString filename; + TestCsmModel model; + AlternativeTestCsmModel altModel; + + void SetUp() override { + TempTestingFiles::SetUp(); + + // Create and populate test ISDs + json isd; + isd["test_param_one"] = 1.0; + isd["test_param_two"] = 2.0; + + isdPath = tempDir.path() + "/default.json"; + std::ofstream file(isdPath.toStdString()); + file << isd; + file.flush(); + + json altIsd; + altIsd["test_param_one"] = 1.0; + altIsd["test_param_two"] = 2.0; + altIsd["test_param_three"] = 3.0; + altIsd["test_param_four"] = 4.0; + + altIsdPath = tempDir.path() + "/alternate.json"; + std::ofstream altFile(altIsdPath.toStdString()); + altFile << altIsd; + altFile.flush(); + std::ifstream cubeLabel("data/threeImageNetwork/cube1.pvl"); + cubeLabel >> label; + testCube = new Cube(); + filename = tempDir.path() + "/csminitCube.cub"; + testCube->fromLabel(filename, label, "rw"); + testCube->close(); + + plugin = csm::Plugin::findPlugin(TestCsmPlugin::PLUGIN_NAME); + } + + void TearDown() override { + if (testCube) { + if (testCube->isOpen()) { + testCube->close(); + } + delete testCube; + testCube = NULL; + } + } +}; + +TEST_F(CSMPluginFixture, CSMInitDefault) { + // Run csminit with defaults for everything besides FROM and ISD + QVector args = { + "from="+filename, + "isd="+isdPath + }; + + UserInterface options(APP_XML, args); + csminit(options); + + testCube->open(filename); + + // Get a model and a state string + StringBlob stateString("","CSMState"); + testCube->read(stateString); + + // Verify contents of the StringBlob's PVL label + PvlObject blobPvl = stateString.Label(); + EXPECT_EQ(stateString.Name().toStdString(), "CSMState"); + EXPECT_EQ(stateString.Type().toStdString(), "String"); + + // Check that the plugin can create a model from the state string + std::string modelName = QString(blobPvl.findKeyword("ModelName")).toStdString(); + EXPECT_TRUE(plugin->canModelBeConstructedFromState(modelName, stateString.string())); + + // Check blob label ModelName and Plugin Name + EXPECT_EQ(QString(blobPvl.findKeyword("PluginName")).toStdString(), plugin->getPluginName()); + EXPECT_EQ(modelName, TestCsmModel::SENSOR_MODEL_NAME); + + // Check the CsmInfo group + ASSERT_TRUE(testCube->hasGroup("CsmInfo")); + PvlGroup &infoGroup = testCube->group("CsmInfo"); + ASSERT_TRUE(infoGroup.hasKeyword("CSMPlatformID")); + EXPECT_EQ(infoGroup["CSMPlatformID"][0].toStdString(), model.getPlatformIdentifier()); + ASSERT_TRUE(infoGroup.hasKeyword("CSMInstrumentId")); + EXPECT_EQ(infoGroup["CSMInstrumentId"][0].toStdString(), model.getSensorIdentifier()); + ASSERT_TRUE(infoGroup.hasKeyword("ReferenceTime")); + EXPECT_EQ(infoGroup["ReferenceTime"][0].toStdString(), model.getReferenceDateAndTime()); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterNames")); + ASSERT_EQ(infoGroup["ModelParameterNames"].size(), 2); + EXPECT_EQ(infoGroup["ModelParameterNames"][0].toStdString(), TestCsmModel::PARAM_NAMES[0]); + EXPECT_EQ(infoGroup["ModelParameterNames"][1].toStdString(), TestCsmModel::PARAM_NAMES[1]); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterUnits")); + ASSERT_EQ(infoGroup["ModelParameterUnits"].size(), 2); + EXPECT_EQ(infoGroup["ModelParameterUnits"][0].toStdString(), TestCsmModel::PARAM_UNITS[0]); + EXPECT_EQ(infoGroup["ModelParameterUnits"][1].toStdString(), TestCsmModel::PARAM_UNITS[1]); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterTypes")); + ASSERT_EQ(infoGroup["ModelParameterTypes"].size(), 2); + EXPECT_EQ(infoGroup["ModelParameterTypes"][0].toStdString(), "FICTITIOUS"); + EXPECT_EQ(infoGroup["ModelParameterTypes"][1].toStdString(), "REAL"); + + // Check the Kernels group + ASSERT_TRUE(testCube->hasGroup("Kernels")); + PvlGroup &kernGroup = testCube->group("Kernels"); + EXPECT_TRUE(kernGroup.hasKeyword("ShapeModel")); +} + +TEST_F(CSMPluginFixture, CSMinitRunTwice) { + // Run csminit twice in a row. Verify that end result is as if csminit were + // only run once with the second arguments. + QVector args = { + "from="+filename, + "isd="+isdPath}; + + UserInterface options(APP_XML, args); + + csminit(options); + + QVector altArgs = { + "from="+filename, + "isd="+altIsdPath, + "modelName="+QString::fromStdString(AlternativeTestCsmModel::SENSOR_MODEL_NAME)}; + + UserInterface altOptions(APP_XML, altArgs); + + csminit(altOptions); + + testCube->open(filename); + + StringBlob stateString("", "CSMState"); + testCube->read(stateString); + PvlObject blobPvl = stateString.Label(); + + // Verify contents of the StringBlob's PVL label + EXPECT_EQ(stateString.Name().toStdString(), "CSMState"); + EXPECT_EQ(stateString.Type().toStdString(), "String"); + + // Check blob label ModelName and Plugin Name + EXPECT_EQ(QString(blobPvl.findKeyword("PluginName")).toStdString(), plugin->getPluginName()); + EXPECT_EQ(QString(blobPvl.findKeyword("ModelName")).toStdString(), AlternativeTestCsmModel::SENSOR_MODEL_NAME); + + // Check that the plugin can create a model from the state string + std::string modelName = QString(blobPvl.findKeyword("ModelName")).toStdString(); + EXPECT_TRUE(plugin->canModelBeConstructedFromState(modelName, stateString.string())); + + // Make sure there is only one CSMState Blob on the label + PvlObject *label = testCube->label(); + label->deleteObject("String"); + EXPECT_FALSE(label->hasObject("String")); + + // Check that there is only one CsmInfo group + ASSERT_TRUE(testCube->hasGroup("CsmInfo")); + testCube->deleteGroup("CsmInfo"); + ASSERT_FALSE(testCube->hasGroup("CsmInfo")); + + // Check that there is only one ShapeModel + ASSERT_TRUE(testCube->hasGroup("Kernels")); + PvlGroup &kernGroup = testCube->group("Kernels"); + EXPECT_TRUE(kernGroup.hasKeyword("ShapeModel")); + kernGroup.deleteKeyword("ShapeModel"); + EXPECT_FALSE(kernGroup.hasKeyword("ShapeModel")); +} + +TEST_F(CSMPluginFixture, CSMinitMultiplePossibleModels) { + // Test csminit when multiple possible models can be created. First, verify that this will fail + // without specifying the MODELNAME, then specify the MODELNAME and check the results. + + QVector args = { + "from="+filename, + "isd="+altIsdPath}; + + UserInterface options(APP_XML, args); + + // If there are two possible models, csminit will fail and prompt the user to specify the model + // and/or plugin name. + try { + csminit(options); + } + catch (IException &e) { + EXPECT_THAT(e.what(), testing::HasSubstr("Multiple models can be created from the ISD")); + } + + // Re-run with the model name specified + args = { + "from="+filename, + "isd="+altIsdPath, + "modelName="+QString::fromStdString(AlternativeTestCsmModel::SENSOR_MODEL_NAME)}; + + UserInterface betterOptions(APP_XML, args); + csminit(betterOptions); + + testCube->open(filename); + StringBlob stateString("", "CSMState"); + testCube->read(stateString); + PvlObject blobPvl = stateString.Label(); + + // Check blobPvl contents + EXPECT_EQ(stateString.Name().toStdString(), "CSMState"); + EXPECT_EQ(stateString.Type().toStdString(), "String"); + + // Check that the plugin can create a model from the state string + std::string modelName = QString(blobPvl.findKeyword("ModelName")).toStdString(); + EXPECT_TRUE(plugin->canModelBeConstructedFromState(modelName, stateString.string())); + + // check blob label ModelName and Plugin Name + EXPECT_EQ(QString(blobPvl.findKeyword("PluginName")).toStdString(), plugin->getPluginName()); + EXPECT_EQ(QString(blobPvl.findKeyword("ModelName")).toStdString(), AlternativeTestCsmModel::SENSOR_MODEL_NAME); + + // Check the CsmInfo group + ASSERT_TRUE(testCube->hasGroup("CsmInfo")); + PvlGroup &infoGroup = testCube->group("CsmInfo"); + ASSERT_TRUE(infoGroup.hasKeyword("CSMPlatformID")); + EXPECT_EQ(infoGroup["CSMPlatformID"][0].toStdString(), altModel.getPlatformIdentifier()); + ASSERT_TRUE(infoGroup.hasKeyword("CSMInstrumentId")); + EXPECT_EQ(infoGroup["CSMInstrumentId"][0].toStdString(), altModel.getSensorIdentifier()); + ASSERT_TRUE(infoGroup.hasKeyword("ReferenceTime")); + EXPECT_EQ(infoGroup["ReferenceTime"][0].toStdString(), altModel.getReferenceDateAndTime()); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterNames")); + ASSERT_EQ(infoGroup["ModelParameterNames"].size(), 3); + EXPECT_EQ(infoGroup["ModelParameterNames"][0].toStdString(), AlternativeTestCsmModel::PARAM_NAMES[0]); + EXPECT_EQ(infoGroup["ModelParameterNames"][1].toStdString(), AlternativeTestCsmModel::PARAM_NAMES[1]); + EXPECT_EQ(infoGroup["ModelParameterNames"][2].toStdString(), AlternativeTestCsmModel::PARAM_NAMES[2]); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterUnits")); + ASSERT_EQ(infoGroup["ModelParameterUnits"].size(), 3); + EXPECT_EQ(infoGroup["ModelParameterUnits"][0].toStdString(), AlternativeTestCsmModel::PARAM_UNITS[0]); + EXPECT_EQ(infoGroup["ModelParameterUnits"][1].toStdString(), AlternativeTestCsmModel::PARAM_UNITS[1]); + EXPECT_EQ(infoGroup["ModelParameterUnits"][2].toStdString(), AlternativeTestCsmModel::PARAM_UNITS[2]); + ASSERT_TRUE(infoGroup.hasKeyword("ModelParameterTypes")); + ASSERT_EQ(infoGroup["ModelParameterTypes"].size(), 3); + EXPECT_EQ(infoGroup["ModelParameterTypes"][0].toStdString(), "FICTITIOUS"); + EXPECT_EQ(infoGroup["ModelParameterTypes"][1].toStdString(), "REAL"); + EXPECT_EQ(infoGroup["ModelParameterTypes"][2].toStdString(), "FIXED"); +} + + +TEST_F(CSMPluginFixture, CSMinitFails) { + // Create an ISD that will result in no successful models + json isd; + isd["name"] = "failing_isd"; + isd["test_param_one"] = "value_one"; + isd["test_param_does_not_exist"] = "failing_value"; + + isdPath = tempDir.path() + "/failing.json"; + std::ofstream file(isdPath.toStdString()); + file << isd; + file.flush(); + + QVector args = { + "from="+filename, + "isd="+isdPath}; + + UserInterface options(APP_XML, args); + + // Expect a failure due to being unable to construct any model from the isd + try { + csminit(options); + } + catch (IException &e) { + EXPECT_THAT(e.what(), testing::HasSubstr("No loaded model could be created from the ISD")); + } +} + + +// This test uses the DefaultCube fixture because it has already has attached +// spice data that csminit should remove. +TEST_F(DefaultCube, CSMinitSpiceCleanup) { + // Create an ISD + json isd; + isd["test_param_one"] = 1.0; + isd["test_param_two"] = 2.0; + + QString isdPath = tempDir.path() + "/default.json"; + std::ofstream file(isdPath.toStdString()); + file << isd; + file.flush(); + + QVector args = { + "from="+testCube->fileName(), + "isd="+isdPath}; + QString cubeFile = testCube->fileName(); + + UserInterface options(APP_XML, args); + + testCube->close(); + csminit(options); + Cube outputCube(cubeFile); + + EXPECT_FALSE(outputCube.hasTable("InstrumentPointing")); + EXPECT_FALSE(outputCube.hasTable("InstrumentPosition")); + EXPECT_FALSE(outputCube.hasTable("BodyRotation")); + EXPECT_FALSE(outputCube.hasTable("SunPosition")); + EXPECT_FALSE(outputCube.hasTable("CameraStatistics")); + ASSERT_TRUE(outputCube.hasGroup("Kernels")); + EXPECT_EQ(outputCube.group("Kernels").keywords(), 2); +} + + +// This test uses the DefaultCube fixture because it has already has attached +// spice data that csminit should not remove on a failure. +TEST_F(DefaultCube, CSMinitSpiceNoCleanup) { + // Create an ISD that will result in no successful models + json isd; + isd["test_param_one"] = "value_one"; + isd["test_param_does_not_exist"] = "failing_value"; + + QString isdPath = tempDir.path() + "/default.json"; + std::ofstream file(isdPath.toStdString()); + file << isd; + file.flush(); + + QVector args = { + "from="+testCube->fileName(), + "isd="+isdPath}; + QString cubeFile = testCube->fileName(); + + UserInterface options(APP_XML, args); + + testCube->close(); + // Expect a failure due to being unable to construct any model from the isd + EXPECT_ANY_THROW(csminit(options)); + Cube outputCube(cubeFile); + + // The cube should still be intact and we should still be able to get a camera + EXPECT_NO_THROW(outputCube.camera()); +} diff --git a/isis/tests/FunctionalTestsSpiceinit.cpp b/isis/tests/FunctionalTestsSpiceinit.cpp index 569e27c151759558eaaddc3207c51650e894fbcd..a1eca464318bff08427b2506c9d9e94ab696d83a 100644 --- a/isis/tests/FunctionalTestsSpiceinit.cpp +++ b/isis/tests/FunctionalTestsSpiceinit.cpp @@ -10,6 +10,7 @@ #include "Pvl.h" #include "PvlGroup.h" #include "PvlKeyword.h" +#include "StringBlob.h" #include "TestUtilities.h" #include "FileName.h" @@ -519,6 +520,37 @@ TEST(Spiceinit, TestSpiceinitPadding) { EXPECT_PRED_FORMAT2(AssertQStringsEqual, kernels["EndPadding"].unit(0), "seconds"); } +TEST_F(DefaultCube, TestSpiceinitCsmCleanup) { + // Add stuff from csminit + testCube->putGroup(PvlGroup("CsmInfo")); + StringBlob testBlob("test string", "CSMState"); + testCube->write(testBlob); + + QVector args(0); + UserInterface options(APP_XML, args); + spiceinit(testCube, options); + + EXPECT_FALSE(testCube->hasGroup("CsmInfo")); + EXPECT_ANY_THROW(testCube->read(testBlob)); +} + +TEST_F(DefaultCube, TestSpiceinitCsmNoCleanup) { + // Add stuff from csminit + testCube->putGroup(PvlGroup("CsmInfo")); + StringBlob testBlob("test string", "CSMState"); + testCube->write(testBlob); + + // Mangle the cube so that spiceinit failes + testCube->deleteGroup("Instrument"); + + QVector args(0); + UserInterface options(APP_XML, args); + ASSERT_ANY_THROW(spiceinit(testCube, options)); + + EXPECT_TRUE(testCube->hasGroup("CsmInfo")); + EXPECT_NO_THROW(testCube->read(testBlob)); +} + TEST_F(DemCube, FunctionalTestSpiceinitWebAndShapeModel) { std::istringstream labelStrm(R"( diff --git a/isis/tests/TestCsmModel.cpp b/isis/tests/TestCsmModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1c7645d2c561d45769abb529ad6d2253709f2e90 --- /dev/null +++ b/isis/tests/TestCsmModel.cpp @@ -0,0 +1,487 @@ +#include "TestCsmModel.h" +#include +#include +#include +using json = nlohmann::json; + +// Sensor model Name +const std::string TestCsmModel::SENSOR_MODEL_NAME = "TestCsmModel"; + +// Sensor model Parameter names +const std::vector TestCsmModel::PARAM_NAMES = { + "test_param_one", + "test_param_two" +}; + +// Sensor model Parameter units +const std::vector TestCsmModel::PARAM_UNITS = { + "m", + "rad" +}; + +// Sensor model Parameter Types +const std::vector TestCsmModel::PARAM_TYPES = { + csm::param::FICTITIOUS, + csm::param::REAL +}; + +// Sensor model Parameter sharing criteria +const std::vector TestCsmModel::PARAM_SHARING_CRITERIA = { + csm::SharingCriteria(), + csm::SharingCriteria() +}; + + +/** + * Constructor. Resizes the parameter values list based on the number of PARAM_NAMES. + */ +TestCsmModel::TestCsmModel() { + m_param_values.resize(TestCsmModel::PARAM_NAMES.size(), 0.0); +}; + + +/** + * Default destructor + */ +TestCsmModel::~TestCsmModel() { +}; + + +/** + * Returns the sensor model family. + * + * @return std::string Sensor model family + */ +std::string TestCsmModel::getFamily() const { + return "TestCsmModelFamily"; +} + + +/** + * Returns the version of the sensor model + * + * @return csm::Version sensor model version + */ +csm::Version TestCsmModel::getVersion() const { + return csm::Version(1,0,0); +} + + +/** + * Returns the name of the sensor model. + * + * @return std::string sensor model name + */ +std::string TestCsmModel::getModelName() const { + return TestCsmModel::SENSOR_MODEL_NAME; +} + + +/** + * Returns the pedigree of the sensor model. + * + * @return std::string sensor model pedigree + */ +std::string TestCsmModel::getPedigree() const { + return "TestCsmModelPedigree"; +} + + +/** + * Returns the image identifier. + * + * @return std::string image identifier + */ +std::string TestCsmModel::getImageIdentifier() const { + return "TestCsmModelImageIdentifier"; +} + + +/** + * Does nothing. Empty implementation for test. + * + * @param imageId image identifier + * @param warnings CSM warnings list + */ +void TestCsmModel::setImageIdentifier(const std::string& imageId, + csm::WarningList* warnings) { + // do nothing for test +} + + +/** + * Returns the sensor identifier for the sensor model. + * + * @return std::string sensor identifier + */ +std::string TestCsmModel::getSensorIdentifier() const { + return "TestCsmModelSensorIdentifier"; +} + + +/** + * Returns the platform identifier for the sensor model. + * + * @return std::string platform identifier + */ +std::string TestCsmModel::getPlatformIdentifier() const { + return "TestCsmModel_PlatformIdentifier"; +} + + +/** + * Returns the collection identifier for the sensor model. + * + * @return std::string collection identifier + */ +std::string TestCsmModel::getCollectionIdentifier() const { + return "TestCsmModel_CollectionIdentifier"; +} + + +/** + * Returns the trajectory identifier for the sensor model. + * + * @return std::string trajectory identifier + */ +std::string TestCsmModel::getTrajectoryIdentifier() const { + return "TestCsmModel_TrajectoryIdentifier"; +} + + +/** + * Reeturns the sensor type for the sensor model. + * + * @return std::string sensor type + */ +std::string TestCsmModel::getSensorType() const { + return "TestCsmModel_SensorType"; +} + + +/** + * Returns the sensor mode for the sensor model + * + * @return std::string sensor mode + */ +std::string TestCsmModel::getSensorMode() const { + return "TestCsmModel_SensorMode"; +} + + +/** + * Returns the reference date and time for the sensor model + * + * @return std::string reference date and time + */ +std::string TestCsmModel::getReferenceDateAndTime() const { + return "TestCsmModel_ReferenceDateTime"; +} + + +/** + * Returns the current model state for the sensor model. + * + * @return std::string model state + */ +std::string TestCsmModel::getModelState() const { + json state; + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + state[TestCsmModel::PARAM_NAMES[param_index]] = m_param_values[param_index]; + } + return TestCsmModel::SENSOR_MODEL_NAME + "\n" + state.dump(); +} + + +/** + * Uses the supplied sensor model state to set the steat of the current sensor model. + * + * @param argState the model state + */ +void TestCsmModel::replaceModelState(const std::string& argState) { + // Get the JSON substring + json state = json::parse(argState.substr(argState.find("\n") + 1)); + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + m_param_values[param_index] = state.at(TestCsmModel::PARAM_NAMES[param_index]); + } +} + + +/** + * Constructs and returns a sensor model state from an ISD. + * + * @param isd instrument support data + * + * @return std::string sensor model state + */ +std::string TestCsmModel::constructStateFromIsd(const csm::Isd isd){ + std::string filename = isd.filename(); + std::ifstream isdFile(filename); + + if (isdFile.fail()) { + std::cout << "Could not open file: " << filename << std::endl; + } + + json parsedIsd; + isdFile >> parsedIsd; + // Only extract the first 2 parameters from the file + json state; + for (size_t param_index = 0; param_index < m_param_values.size(); param_index++) { + state[TestCsmModel::PARAM_NAMES[param_index]] = parsedIsd.at(TestCsmModel::PARAM_NAMES[param_index]); + } + return TestCsmModel::SENSOR_MODEL_NAME + "\n" + state.dump(); +} + + +/** + * Returns a default reference point. + * + * @return csm::EcefCoord reference point + */ +csm::EcefCoord TestCsmModel::getReferencePoint() const { + return csm::EcefCoord(0.0, 0.0, 0.0); +} + + +/** + * Does nothing. Minimal implementation for test. + * + * @param groundPt the ground point + */ +void TestCsmModel::setReferencePoint(const csm::EcefCoord& groundPt) { + // do nothing for test +} + + +/** + * Returns the number of sensor model parameters + * + * @return int number of parameters + */ +int TestCsmModel::getNumParameters() const { + return m_param_values.size(); +} + + +/** + * Returns the semsor model parameter name at the provided index + * + * @param index parameter index + * + * @return std::string parameter name + */ +std::string TestCsmModel::getParameterName(int index) const { + return TestCsmModel::PARAM_NAMES[index]; +} + + +/** + * Returns the sensor model parameter units at the provided index + * + * @param index parameter unit index + * + * @return std::string parameter units + */ +std::string TestCsmModel::getParameterUnits(int index) const { + return TestCsmModel::PARAM_UNITS[index]; +} + + +/** + * True if the sensor model has sharable parameters. + * + * @return bool Always returns false + */ +bool TestCsmModel::hasShareableParameters() const { + return false; +} + + +/** + * True if the sensor model parameter at the provided index is sharable. + * + * @param index Parameter index + * + * @return bool Always returns false + */ +bool TestCsmModel::isParameterShareable(int index) const { + return false; +} + + + +/** + * Returns the sharing criteria for the sensor model parameter at the provided index + * + * @param index Parameter index + * + * @return csm::SharingCriteria CSM sharing criteria for the parameter + */ +csm::SharingCriteria TestCsmModel::getParameterSharingCriteria(int index) const { + return TestCsmModel::PARAM_SHARING_CRITERIA[index]; +} + + +/** + * Returns the sensor model parameter value at the provided index. + * + * @param index Parameter index + * + * @return double Value at provided index + */ +double TestCsmModel::getParameterValue(int index) const { + return m_param_values[index]; +} + + +/** + * Set the sensor model parameter at the provided index to the provided + * value. + * + * @param index Parameter index + * @param value Value to set the parameter to + */ +void TestCsmModel::setParameterValue(int index, double value) { + m_param_values[index] = value; +} + + +/** + * Returns the type of the sensor model parameter at the provided index. + * + * @param index Parameter index + * + * @return csm::param::Type Type of parameter + */ +csm::param::Type TestCsmModel::getParameterType(int index) const { + return TestCsmModel::PARAM_TYPES[index]; +} + + +/** + * Does nothing. Minimal implementation for testing + * + * @param index Parameter index + * @param pType Parameter type + */ +void TestCsmModel::setParameterType(int index, csm::param::Type pType) { + // do nothing for test +} + + +/** + * Returns the covariance between the two sensor model parameters at the provided indicies. + * Defaults to identity covariance matrix for testing. + * + * @param index1 First parameter index + * @param index2 Second parameter index + * + * @return double Parameter covariance + */ +double TestCsmModel::getParameterCovariance(int index1, + int index2) const { + // default to identity covariance matrix + if (index1 == index2) { + return 1.0; + } + return 0.0; +} + + +/** + * Does nothing. Minimal implementation for testing. + * + * @param index1 First parameter index + * @param index2 Second parameter index + * @param covariance Covariance between the two parameters + */ +void TestCsmModel::setParameterCovariance(int index1, + int index2, + double covariance) { + // do nothing for test +} + + +/** + * Returns the number of geometric correction switches. + * + * @return int Number of geometric correction switches. + */ +int TestCsmModel::getNumGeometricCorrectionSwitches() const { + return 0; +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * + * @return std::string Geometric correction + */ +std::string TestCsmModel::getGeometricCorrectionName(int index) const { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "TestCsmModel::getGeometricCorrectionName"); +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * @param value Value to set + * @param pType Parameter type + */ +void TestCsmModel::setGeometricCorrectionSwitch(int index, + bool value, + csm::param::Type pType) { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "TestCsmModel::setGeometricCorrectionSwitch"); +} + + +/** + * Always throws an error, as no geometric correction switches exist for this class. + * + * @param index Geometric correction index + * + * @return bool If the geometric correction switch can be accessed. + */ +bool TestCsmModel::getGeometricCorrectionSwitch(int index) const { + throw csm::Error(csm::Error::INDEX_OUT_OF_RANGE, "Index out of range.", + "TestCsmModel::getGeometricCorrectionSwitch"); +} + + +/** + * Returns the cross covariance matrix. + * + * @param comparisonModel The geometric model to compare with. + * @param pSet Set of parameters to use + * @param otherModels Not used. + * + * @return std::vector covariance matrix + */ +std::vector TestCsmModel::getCrossCovarianceMatrix( + const csm::GeometricModel& comparisonModel, + csm::param::Set pSet, + const csm::GeometricModel::GeometricModelList& otherModels) const { + const std::vector& rowIndices = getParameterSetIndices(pSet); + size_t numRows = rowIndices.size(); + const std::vector& colIndices = comparisonModel.getParameterSetIndices(pSet); + size_t numCols = colIndices.size(); + std::vector covariance(numRows * numCols, 0.0); + + if (&comparisonModel == this) { + for (size_t rowIndex = 0; rowIndex < numRows; numRows++) { + for (size_t colIndex = 0; colIndex < numCols; colIndex++) { + covariance[rowIndex * numCols + colIndex] = getParameterCovariance(rowIndices[rowIndex], + colIndices[colIndex]); + } + } + } + + return covariance; +} + + diff --git a/isis/tests/TestCsmModel.h b/isis/tests/TestCsmModel.h new file mode 100644 index 0000000000000000000000000000000000000000..2ea6edfa6d48e9c4ac299eedff69bf462cbe5d94 --- /dev/null +++ b/isis/tests/TestCsmModel.h @@ -0,0 +1,79 @@ +#ifndef TestCsmModel_h +#define TestCsmModel_h + +#include + +#include "csm/GeometricModel.h" +#include "csm/Plugin.h" +#include "csm/Version.h" + +#include + +/** + * A Test CSM (Community Sensor Model) Sensor Model used to test + * CSM sensor model support in ISIS. + * + * @author 2020-12-08 Kristin Berry + */ +class TestCsmModel : public csm::GeometricModel { + public: + // Static variables that describe the model + static const std::string SENSOR_MODEL_NAME; + static const std::vector PARAM_NAMES; + static const std::vector PARAM_UNITS; + static const std::vector PARAM_TYPES; + static const std::vector PARAM_SHARING_CRITERIA; + + TestCsmModel(); + ~TestCsmModel(); + // csm::Model methods + std::string getFamily() const; + csm::Version getVersion() const; + std::string getModelName() const; + std::string getPedigree() const; + std::string getImageIdentifier() const; + void setImageIdentifier(const std::string& imageId, + csm::WarningList* warnings = NULL); + std::string getSensorIdentifier() const; + std::string getPlatformIdentifier() const; + std::string getCollectionIdentifier() const; + std::string getTrajectoryIdentifier() const; + std::string getSensorType() const; + std::string getSensorMode() const; + std::string getReferenceDateAndTime() const; + std::string getModelState() const; + void replaceModelState(const std::string& argState); + std::string constructStateFromIsd(const csm::Isd stringIsd); + // csm::GeometricModel methods + csm::EcefCoord getReferencePoint() const; + void setReferencePoint(const csm::EcefCoord& groundPt); + int getNumParameters() const; + std::string getParameterName(int index) const; + std::string getParameterUnits(int index) const; + bool hasShareableParameters() const; + bool isParameterShareable(int index) const; + csm::SharingCriteria getParameterSharingCriteria(int index) const; + double getParameterValue(int index) const; + void setParameterValue(int index, double value); + csm::param::Type getParameterType(int index) const; + void setParameterType(int index, csm::param::Type pType); + double getParameterCovariance(int index1, + int index2) const; + void setParameterCovariance(int index1, + int index2, + double covariance); + int getNumGeometricCorrectionSwitches() const; + std::string getGeometricCorrectionName(int index) const; + void setGeometricCorrectionSwitch(int index, + bool value, + csm::param::Type pType); + bool getGeometricCorrectionSwitch(int index) const; + std::vector getCrossCovarianceMatrix( + const csm::GeometricModel& comparisonModel, + csm::param::Set pSet = csm::param::VALID, + const csm::GeometricModel::GeometricModelList& otherModels = GeometricModel::GeometricModelList()) const; + + private: + std::vector m_param_values; //! Parameter values associated with the sensor model +}; +#endif diff --git a/isis/tests/TestCsmPlugin.cpp b/isis/tests/TestCsmPlugin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e5d3274d31bd6b7372d43efe385b04172fbf26c0 --- /dev/null +++ b/isis/tests/TestCsmPlugin.cpp @@ -0,0 +1,296 @@ +#include + +#include "TestCsmPlugin.h" +#include "TestCsmModel.h" +#include "AlternativeTestCsmModel.h" + +// Static Instance of itself +const TestCsmPlugin TestCsmPlugin::m_registeredPlugin; + +// Declaration of static variables +const std::string TestCsmPlugin::PLUGIN_NAME = "TestCsmPlugin"; +const std::string TestCsmPlugin::MANUFACTURER_NAME = "TestCsmPluginCreator"; +const std::string TestCsmPlugin::RELEASE_DATE = "20201208"; +const int TestCsmPlugin::N_SENSOR_MODELS = 2; + +/** + * Default constructor + */ +TestCsmPlugin::TestCsmPlugin() { + // deliberately blank for testing +} + + +/** + * Default destructor + */ +TestCsmPlugin::~TestCsmPlugin() {} + + +/** + * Gets the name of the plugin. + * + * @return std::string name of the plugin + */ +std::string TestCsmPlugin::getPluginName() const { + return TestCsmPlugin::PLUGIN_NAME; +} + + +/** + * Gets the name of the manufacturer of the plugin. + * + * @return std::string the name of the manufacturer of the plugin + */ +std::string TestCsmPlugin::getManufacturer() const { + return TestCsmPlugin::MANUFACTURER_NAME; +} + + +/** + * Gets the release date of the plugin. + * + * @return std::string release date + */ +std::string TestCsmPlugin::getReleaseDate() const { + return TestCsmPlugin::RELEASE_DATE; +} + + +/** + * Returns the version of CSM the Plugin uses. + * + * @return csm::Version CSM version plugin uses + */ +csm::Version TestCsmPlugin::getCsmVersion() const { + return csm::Version(3,0,3); +} + + +/** + * Returns the number of sensor models in the plugin + * + * @return size_t Number of sensor models in the plugin + */ +size_t TestCsmPlugin::getNumModels() const { + return TestCsmPlugin::N_SENSOR_MODELS; +} + + +/** + * Returns the model name at the given index. + * + * @param modelIndex The index number for the sensor model + * + * @return std::string model name + */ +std::string TestCsmPlugin::getModelName(size_t modelIndex) const { + std::vector supportedModelNames = { + TestCsmModel::SENSOR_MODEL_NAME, + AlternativeTestCsmModel::SENSOR_MODEL_NAME}; + return supportedModelNames[modelIndex]; +} + + +/** + * Returns the sensor model family at the given index. + * + * @param modelIndex the index number for the sensor model family + * + * @return std::string sensor model family + */ +std::string TestCsmPlugin::getModelFamily(size_t modelIndex) const { + return "TestModelFamily"; +} + + +/** + * Returns the CSM sensor model version for a given model. + * + * @param modelName the model name + * + * @return csm::Version the version of the csm sensor model + */ +csm::Version TestCsmPlugin::getModelVersion(const std::string& modelName) const { + return csm::Version(1,0,0); +} + + +/** + * Tests if the sensor model can be created from a given state. + * + * @param modelName the model name + * @param modelState the model state + * @param warnings the warning list + * + * @return bool if the model can be constructed from the state + */ +bool TestCsmPlugin::canModelBeConstructedFromState(const std::string& modelName, + const std::string& modelState, + csm::WarningList* warnings) const { + try { + csm::Model *model = constructModelFromState(modelState, warnings); + return static_cast(model); + } + catch (std::exception &e) { + // No op + } + return false; +} + + +/** + * Checks to see if the CSM sensor model can be constructed from + * a given ISD. + * + * @param imageSupportData isd for the image + * @param modelName name of the sensor model + * @param warnings warnings list + * + * @return bool true if the model can be constructed from the isd + */ +bool TestCsmPlugin::canModelBeConstructedFromISD( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings) const { + try { + csm::Model *model = + constructModelFromISD(imageSupportData, modelName, warnings); + return static_cast(model); + } catch (std::exception &e) { + // no op + } + return false; +} + + +/** + * True if the ISD can be converted to a state. + * + * @param imageSupportData the image support data + * @param modelName the name of the model + * @param warnings the warnings list + * + * @return bool true if the ISD can be converted to a state. + */ +bool TestCsmPlugin::canISDBeConvertedToModelState( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings) const{ + try { + convertISDToModelState(imageSupportData, modelName, warnings); + } + catch (std::exception &e) { + // no op + return false; + } + return true; +} + + +/** + * Conver an ISD (Image Support Data) to a model state. + * + * @param imageSupportData The ISD + * @param modelName The name of the model. + * @param warnings The warnings list. + * + * @return std::string the model state converted from the ISD + */ +std::string TestCsmPlugin::convertISDToModelState( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings) const { + csm::Model *model = + constructModelFromISD(imageSupportData, modelName, warnings); + return model->getModelState(); +} + + +/** + * Extracts and returns the model name from the model state. + * + * @param modelState State of the sensor model + * @param warnings The warnings list + * + * @return std::string The sensor model name + */ +std::string TestCsmPlugin::getModelNameFromModelState( + const std::string& modelState, + csm::WarningList* warnings) const { + + std::string name = modelState.substr(0, modelState.find("\n")); + + if (name == "") { + 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 = "TestCsmPlugin::getModelNameFromModelState"; + csm::Error csmErr(aErrorType, aMessage, aFunction); + throw(csmErr); + } + return name; +} + + +/** + * Creates and returns a sensor model from a state string. + * + * @param modelState State of the sensor model + * @param warnings The csm warnings list + * + * @return csm::Model* The constructed sensor model + */ +csm::Model* TestCsmPlugin::constructModelFromState( + const std::string& modelState, + csm::WarningList* warnings) const { + + std::string modelName = getModelNameFromModelState(modelState, warnings); + if (modelName == TestCsmModel::SENSOR_MODEL_NAME) { + TestCsmModel *model = new TestCsmModel(); + model->replaceModelState(modelState); + return model; + } + else if (modelName == AlternativeTestCsmModel::SENSOR_MODEL_NAME) { + AlternativeTestCsmModel *model = new AlternativeTestCsmModel(); + model->replaceModelState(modelState); + return model; + } + else { + csm::Error::ErrorType errorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED; + std::string msg = "TstCsmPlugin failed to construct model from State"; + std::string func = "TestCsmPlugin::constructModelFromState"; + throw csm::Error(errorType, msg, func); + } +} + + +/** + * Constructs and returns a sensor model from an ISD. + * + * @param imageSupportData The image support data for an image + * @param modelName The sensor model name + * + * @return csm::Model* the model constructed from the ISD + */ +csm::Model* TestCsmPlugin::constructModelFromISD( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* ) const { + if (modelName == TestCsmModel::SENSOR_MODEL_NAME) { + TestCsmModel *model = new TestCsmModel(); + model->replaceModelState(model->constructStateFromIsd(imageSupportData)); + return model; + } + else if (modelName == AlternativeTestCsmModel::SENSOR_MODEL_NAME) { + AlternativeTestCsmModel *model = new AlternativeTestCsmModel(); + model->replaceModelState(model->constructStateFromIsd(imageSupportData)); + return model; + } + else { + csm::Error::ErrorType errorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED; + std::string msg = "TstCsmPlugin failed to construct model from ISD"; + std::string func = "TestCsmPlugin::constructModelFromIsd"; + throw csm::Error(errorType, msg, func); + } +} + diff --git a/isis/tests/TestCsmPlugin.h b/isis/tests/TestCsmPlugin.h new file mode 100644 index 0000000000000000000000000000000000000000..6367af32b1c1d714be36f844c62d571db43e24e2 --- /dev/null +++ b/isis/tests/TestCsmPlugin.h @@ -0,0 +1,80 @@ +#ifndef TestCsmPlugin_h +#define TestCsmPlugin_h + +#include + +#include "csm/Plugin.h" +#include "csm/Model.h" +#include "csm/Version.h" + +#include + +/** + * Test Community Sensor Model (CSM) plugin class. + * + * @author 2020-12-08 Kristin Berry + */ +class TestCsmPlugin : public csm::Plugin { + public: + // Static variables that describe the plugin + static const std::string PLUGIN_NAME; + static const std::string MANUFACTURER_NAME; + static const std::string RELEASE_DATE; + static const int N_SENSOR_MODELS; + + TestCsmPlugin(); + ~TestCsmPlugin(); + + std::string getPluginName() const; + + std::string getManufacturer() const; + + std::string getReleaseDate() const; + + csm::Version getCsmVersion() const; + + size_t getNumModels() const; + + std::string getModelName(size_t modelIndex) const; + + std::string getModelFamily(size_t modelIndex) const; + + csm::Version getModelVersion(const std::string& modelName) const; + + bool canModelBeConstructedFromState(const std::string& modelName, + const std::string& modelState, + csm::WarningList* warnings = NULL) const; + + bool canModelBeConstructedFromISD( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings = NULL) const; + + bool canISDBeConvertedToModelState( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings = NULL) const; + + std::string convertISDToModelState( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings = NULL) const; + + csm::Model* constructModelFromState( + const std::string& modelState, + csm::WarningList* warnings = NULL) const; + + csm::Model* constructModelFromISD( + const csm::Isd& imageSupportData, + const std::string& modelName, + csm::WarningList* warnings = NULL) const; + + std::string getModelNameFromModelState( + const std::string& modelState, + csm::WarningList* warnings = NULL) const; + + private: + static const TestCsmPlugin m_registeredPlugin; //! static instance of plugin +}; + +#endif