Skip to content

Commit

Permalink
Adds csminit and csmpt as prototype CSM support (#4143)
Browse files Browse the repository at this point in the history
* Partial csminit code

* Update csminit to get compiling, update label based on discussion to store csm information in the Archive group, and write the state string out to the cube

* Removed Thanksgiving-themed debug output

* Updated StringBlob override methods

* Fixed some build errors

* Fixed header

* Working?

* Now working?

* First pass at csmpt application

* Better error checking

* Modified to use the plugin specification

* Addressed PR comments

Co-authored-by: Kristin <[email protected]>
  • Loading branch information
jessemapel and krlberry authored Nov 25, 2020
1 parent c46a426 commit 2a2e56f
Show file tree
Hide file tree
Showing 14 changed files with 794 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
7 changes: 7 additions & 0 deletions isis/src/base/apps/csminit/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
ifeq ($(ISISROOT), $(BLANK))
.SILENT:
error:
echo "Please set ISISROOT";
else
include $(ISISROOT)/make/isismake.apps
endif
221 changes: 221 additions & 0 deletions isis/src/base/apps/csminit/csminit.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/** 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 <QList>
#include <QString>
#include <QStringList>

#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);

// TESTING
// We have to call this to get the plugin list loaded right now
try {
Camera *cam = CameraFactory::Create(*cube);
delete cam;
}
catch(...) {
// Noop
}
// END TESTING

QString isdFilePath = ui.GetFileName("ISD");

QList<QStringList> 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_);
// std::cout << "No model found" << std::endl;
}

// 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();
// string modelState = "TestModel\nThis is the test model state.";

// TODO Just do spiceinit clean-up routine instead
try {
cube->camera();
QString message = "Input cube [" + ui.GetFileName("FROM") + "]. "
"Already has an ISIS camera model associated with it. CSM "
"models cannot be added to cubes with an ISIS camera model.";
throw IException(IException::Programmer, message, _FILEINFO_);
}
catch(IException &e) {
// no operation, continue
}

// 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");
if (instrumentGroup.hasKeyword("TargetName")) {
instrumentGroup.deleteKeyword("TargetName");
}
instrumentGroup += PvlKeyword("TargetName", ui.GetString("TARGETNAME"));
}

// Popualte the Archive group with useful information
if (!cube->hasGroup("Archive")) {
cube->putGroup(PvlGroup("Archive"));
}
PvlGroup &archiveGroup = cube->group("Archive");
if (archiveGroup.hasKeyword("CSMPlatformID")) {
archiveGroup.deleteKeyword("CSMPlatformID");
}
archiveGroup += PvlKeyword("CSMPlatformID",
// "TestPlatformID");
QString::fromStdString(model->getPlatformIdentifier()));
if (archiveGroup.hasKeyword("CSMInstrumentId")) {
archiveGroup.deleteKeyword("CSMInstrumentId");
}
archiveGroup += PvlKeyword("CSMInstrumentId",
// "TestInstrumentID");
QString::fromStdString(model->getSensorIdentifier()));
if (archiveGroup.hasKeyword("ReferenceTime")) {
archiveGroup.deleteKeyword("ReferenceTime");
}
archiveGroup += PvlKeyword("ReferenceTime",
// "TestReferenceTime");
QString::fromStdString(model->getReferenceDateAndTime()));

// 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 (kernelsGroup.hasKeyword("ShapeModel")) {
kernelsGroup.deleteKeyword("ShapeModel");
}
if (ui.WasEntered("SHAPEMODEL")) {
// TODO validate the shapemodel
kernelsGroup += PvlKeyword("ShapeModel", ui.GetString("SHAPEMODEL"));
}
else {
kernelsGroup += PvlKeyword("ShapeModel", "Ellipsoid");
}

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", "TestModelName");
blobLabel += PvlKeyword("ModelName", QString::fromStdString(model->getModelName()));
// blobLabel += PvlKeyword("PluginName", "TestPluginName");
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);
}

}
18 changes: 18 additions & 0 deletions isis/src/base/apps/csminit/csminit.h
Original file line number Diff line number Diff line change
@@ -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
110 changes: 110 additions & 0 deletions isis/src/base/apps/csminit/csminit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>

<application name="csminit" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://isis.astrogeology.usgs.gov/Schemas/Application/application.xsd">
<brief>
Attach a CSM Model state to a cube
</brief>

<description>
<p>
This program takes an ISD or state string and attempts to create
a valid CSM Model from the loaded CSM Libraries. Then, it attaches the
state string from the CSM Model to the cube for later use. If there are
multiple valid CSM Models that can be instantiated, then this program
will error and the user will have to re-run it with a specific model
and/or plugin name.
</p>
</description>

<category>
<categoryItem>Cameras</categoryItem>
</category>

<history>
<change name="Jesse Mapel" date="2020-11-18">
Original Version
</change>
</history>

<groups>
<group name="Input Files">
<parameter name="FROM">
<type>cube</type>
<fileMode>input</fileMode>
<brief>
The input cube that the state string will be attached to.
</brief>
<description>
The input cube that the state string will be attached to.
</description>
<filter>*.cub</filter>
</parameter>

<parameter name="ISD">
<type>filename</type>
<fileMode>input</fileMode>
<brief>
The ISD file that will be used.
</brief>
<description>
The ISD file that will be used to create the model state string.
</description>
</parameter>
</group>

<group name="Target Specification">
<parameter name="TARGETNAME">
<type>string</type>
<internalDefault>none</internalDefault>
<brief>
The name of the body observed by the image.
</brief>
<description>
The name of the body observed by the image. If not entered, then the
target already listed on the label will be used.
</description>
</parameter>

<parameter name="SHAPEMODEL">
<type>filename</type>
<internalDefault>none</internalDefault>
<brief>
The shapemodel to represent the surface of the observed body.
</brief>
<description>
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.
</description>
</parameter>
</group>

<group name="Model Specification">
<parameter name="PLUGINNAME">
<type>string</type>
<internalDefault>none</internalDefault>
<brief>
The name of the plugin to use.
</brief>
<description>
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.
</description>
</parameter>

<parameter name="MODELNAME">
<type>string</type>
<internalDefault>none</internalDefault>
<brief>
The name of the model to use.
</brief>
<description>
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.
</description>
</parameter>
</group>
</groups>
</application>
Loading

0 comments on commit 2a2e56f

Please sign in to comment.