diff --git a/CHANGELOG.md b/CHANGELOG.md index b84badcae3a523fd88be276c5ecf5ca2ed175451..e3d555def1500fbf152a9780d4fee42dba878e4a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -70,6 +70,7 @@ release. - Fixed gllssi2isis to support V1.1 data [#5396](https://github.com/DOI-USGS/ISIS3/issues/5396) ### Added +- Added option to save and apply bundle adjustment values in `jigsaw` [#4474](https://github.com/DOI-USGS/ISIS3/issues/4474) - Added versioned default values to lrowacphomap's PHOALGO and PHOPARCUBE parameters and updated lrowacphomap to handle them properly. [#5452](https://github.com/DOI-USGS/ISIS3/pull/5452) ## [8.2.0] - 2024-04-18 diff --git a/environment.yml b/environment.yml index 54e67abafe4d8a8b99feb50246f6ec737f3bfac1..9fca007e686de895fb5b174725303baf7e81f2b2 100644 --- a/environment.yml +++ b/environment.yml @@ -27,6 +27,7 @@ dependencies: - graphviz - conda-forge::gsl >=2.6, <2.7 - hdf5 + - highfive - icu - inja - jama diff --git a/isis/src/base/objs/SpiceRotation/SpiceRotation.cpp b/isis/src/base/objs/SpiceRotation/SpiceRotation.cpp index c5b6c55a595de35c0fa74ec865b51b12f45ca9fb..c4af96fdb75c5bdbde5c40e384e83254473aa6a6 100644 --- a/isis/src/base/objs/SpiceRotation/SpiceRotation.cpp +++ b/isis/src/base/objs/SpiceRotation/SpiceRotation.cpp @@ -1880,6 +1880,11 @@ namespace Isis { const std::vector<double> &coeffAng3, const Source type) { + if (type == PolyFunctionOverSpice && !m_orientation) { + QString msg = "The quaternion SPICE tables are no longer available. " + "Either re-run spiceinit or set OVEREXISTING to False."; + throw IException(IException::User, msg, _FILEINFO_); + } NaifStatus::CheckErrors(); Isis::PolynomialUnivariate function1(p_degree); Isis::PolynomialUnivariate function2(p_degree); @@ -3253,7 +3258,7 @@ namespace Isis { * @see SpiceRotation::SetEphemerisTime */ void SpiceRotation::setEphemerisTimeMemcache() { - // If the cache has only one rotation, set it + // If the cache has only one rotation, set it NaifStatus::CheckErrors(); if (p_cacheTime.size() == 1) { p_CJ = m_orientation->getRotations()[0].toRotationMatrix(); @@ -3487,23 +3492,23 @@ namespace Isis { p_av[index] += cacheVelocity[index]; } - if (angles[0] <= -1 * pi_c()) { - angles[0] += twopi_c(); - } - else if (angles[0] > pi_c()) { - angles[0] -= twopi_c(); - } + if (angles[0] <= -1 * pi_c()) { + angles[0] += twopi_c(); + } + else if (angles[0] > pi_c()) { + angles[0] -= twopi_c(); + } - if (angles[2] <= -1 * pi_c()) { - angles[2] += twopi_c(); - } - else if (angles[2] > pi_c()) { - angles[2] -= twopi_c(); - } + if (angles[2] <= -1 * pi_c()) { + angles[2] += twopi_c(); + } + else if (angles[2] > pi_c()) { + angles[2] -= twopi_c(); + } - eul2m_c((SpiceDouble) angles[2], (SpiceDouble) angles[1], (SpiceDouble) angles[0], - p_axis3, p_axis2, p_axis1, - (SpiceDouble( *)[3]) &p_CJ[0]); + eul2m_c((SpiceDouble) angles[2], (SpiceDouble) angles[1], (SpiceDouble) angles[0], + p_axis3, p_axis2, p_axis1, + (SpiceDouble( *)[3]) &p_CJ[0]); } diff --git a/isis/src/base/objs/Table/Table.cpp b/isis/src/base/objs/Table/Table.cpp index 330d422815467a23ea04b2cdf266b57189baff69..34b3aa1549d417b3e2a3b36b175ab9db516de35f 100644 --- a/isis/src/base/objs/Table/Table.cpp +++ b/isis/src/base/objs/Table/Table.cpp @@ -7,6 +7,7 @@ find files of those names at the top level of this repository. **/ #include "Table.h" #include <fstream> +#include <sstream> #include <string> #include "Blob.h" @@ -137,6 +138,63 @@ namespace Isis { } } + /** + * This constructor takes in a string to create a Table object. + * + * @param tableName The name of the Table to be read + * @param tableStr The table string + * @param fieldDelimiter The delimiter to separate fields with + */ + Table::Table(const QString &tableName, const std::string &tableString, const char &fieldDelimiter) { + p_name = tableName; + + std::stringstream tableStream; + tableStream << tableString; + + std::vector<std::string> tableLinesStringList; + std::string line; + while(std::getline(tableStream, line, '\n')) { + tableLinesStringList.push_back(line); + } + + int numOfFieldValues = tableLinesStringList.size() - 1; // minus the header line + + std::string fieldNamesLineString = tableLinesStringList.front(); + std::stringstream fieldNamesStringStream; + fieldNamesStringStream << fieldNamesLineString; + + std::vector<QString> fieldNames; + std::string fieldNameString; + while(std::getline(fieldNamesStringStream, fieldNameString, fieldDelimiter)) { + fieldNames.push_back(QString::fromStdString(fieldNameString)); + } + + // Clear error flags and set pointer back to beginning + tableStream.clear(); + tableStream.seekg(0, ios::beg); + + // Add records to table + std::string recordString; + int index = 0; + while(std::getline(tableStream, recordString, '\n')) { + // skip first line bc that's the header line + if (index == 0) { + index++; + continue; + } + + TableRecord tableRecord(recordString, fieldDelimiter, fieldNames, numOfFieldValues); + p_record = tableRecord; + this->operator+=(tableRecord); + index++; + } + + // Add fields + for (int f = 0; f < p_record.Fields(); f++) { + p_label.addGroup(p_record[f].pvlGroup()); + } + } + /** * Initialize a Table from a Blob that has been read from a file. diff --git a/isis/src/base/objs/Table/Table.h b/isis/src/base/objs/Table/Table.h index c5db2335680e1a42c64e338327b7ccf22c13002f..7179141ed6f8ef12a8304a92758befbaf7d8f4a5 100644 --- a/isis/src/base/objs/Table/Table.h +++ b/isis/src/base/objs/Table/Table.h @@ -79,6 +79,7 @@ namespace Isis { Table(const QString &tableName, const QString &file, const Pvl &fileHeader); Table(const Table &other); + Table(const QString &tableName, const std::string &tableString, const char &fieldDelimiter); Table &operator=(const Isis::Table &other); ~Table(); diff --git a/isis/src/base/objs/TableRecord/TableRecord.cpp b/isis/src/base/objs/TableRecord/TableRecord.cpp index 12384d46c49cb8af3004346b8179581c157cb52e..7e72df99155d5a0c1ce8749b52482eaa8eb5be06 100644 --- a/isis/src/base/objs/TableRecord/TableRecord.cpp +++ b/isis/src/base/objs/TableRecord/TableRecord.cpp @@ -8,6 +8,7 @@ find files of those names at the top level of this repository. **/ #include "TableRecord.h" #include <iostream> +#include <sstream> #include <string> #include <vector> @@ -21,6 +22,29 @@ namespace Isis { TableRecord::TableRecord(){ } + /** + * TableRecord constructor + * + * @param tableRecordStr Table record string + * @param fieldDelimiter The delimiter to separate fields with + * @param fieldNames Table header names + * @param numOfFieldValues Number of fields (rows) + */ + TableRecord::TableRecord(std::string tableRecordStr, char fieldDelimiter, + std::vector<QString> fieldNames, int numOfFieldValues) { + std::stringstream tableRecordStream; + tableRecordStream << tableRecordStr; + + std::string fieldStr; + int i = 0; + while(std::getline(tableRecordStream, fieldStr, fieldDelimiter)) { + TableField tableField(fieldNames[i], TableField::Double); + tableField = std::stod(fieldStr); // convert string to double + this->operator+=(tableField); + i++; + } + } + //! Destroys the TableRecord object TableRecord::~TableRecord() { } @@ -155,7 +179,7 @@ namespace Isis { Isis::TableField &field = p_fields[f]; field = (void *)&buf[sbyte]; sbyte += field.bytes(); - } + } } /** diff --git a/isis/src/base/objs/TableRecord/TableRecord.h b/isis/src/base/objs/TableRecord/TableRecord.h index ed610f38dbbb63d8b6f4f7ff5dc1c1b4094862ee..0df1a3cff2ffddd525475890baeb8f3ccc1e3d62 100644 --- a/isis/src/base/objs/TableRecord/TableRecord.h +++ b/isis/src/base/objs/TableRecord/TableRecord.h @@ -38,6 +38,8 @@ namespace Isis { class TableRecord { public: TableRecord(); + TableRecord(std::string tableRecordStr, char fieldDelimiter, + std::vector<QString> fieldNames, int numOfFieldValues); ~TableRecord(); diff --git a/isis/src/control/apps/jigsaw/jigsaw.cpp b/isis/src/control/apps/jigsaw/jigsaw.cpp index 57f39a6370a2da11020f54280006fab5894b3bd2..ce9a4064b1e115f23f5cc397bc38e6c6f1d73065 100644 --- a/isis/src/control/apps/jigsaw/jigsaw.cpp +++ b/isis/src/control/apps/jigsaw/jigsaw.cpp @@ -7,6 +7,11 @@ find files of those names at the top level of this repository. **/ /* SPDX-License-Identifier: CC0-1.0 */ #include <iostream> +#include <highfive/H5File.hpp> +#include <highfive/H5DataType.hpp> +#include <highfive/H5DataSet.hpp> +#include <highfive/H5Group.hpp> +#include <vector> #include <QDir> #include <QList> @@ -14,6 +19,7 @@ find files of those names at the top level of this repository. **/ #include <QSharedPointer> #include <QString> + #include "Blob.h" #include "BundleAdjust.h" #include "BundleObservationSolveSettings.h" @@ -35,6 +41,8 @@ find files of those names at the top level of this repository. **/ #include "jigsaw.h" using namespace std; +using namespace HighFive; + namespace Isis { @@ -47,6 +55,77 @@ namespace Isis { void jigsaw(UserInterface &ui, Pvl *log) { + QString cubeList = ui.GetFileName("FROMLIST"); + + // Check for ADJUSTMENT_INPUT file and apply + try { + if (ui.WasEntered("ADJUSTMENT_INPUT")) { + + // Set default values + ui.Clear("TWIST"); + ui.PutBoolean("TWIST", false); + ui.PutBoolean("BUNDLEOUT_TXT", false); + ui.PutBoolean("IMAGESCSV", false); + ui.PutBoolean("OUTPUT_CSV", false); + ui.PutBoolean("RESIDUALS_CSV", false); + ui.PutBoolean("LIDAR_CSV", false); + ui.PutBoolean("OUTADJUSTMENTH5", false); + + File fileRead(ui.GetFileName("ADJUSTMENT_INPUT").toStdString(), File::ReadOnly); + SerialNumberList *snList = new SerialNumberList(cubeList); + + QString jigApplied = "JigApplied = " + Isis::iTime::CurrentLocalTime(); + + for (int i = 0; i < snList->size(); i++) { + Process p; + CubeAttributeInput inAtt; + Cube *c = p.SetInputCube(snList->fileName(i), inAtt, ReadWrite); + + if (c->hasBlob("CSMState", "String")) { + QString msg = "Unable to apply bundle adjustment values to cubes with CSMState blobs."; + throw IException(IException::User, msg, _FILEINFO_); + } + + QString serialNumber = snList->serialNumber(i); + QString cmatrixName = "InstrumentPointing"; + QString spvectorName = "InstrumentPosition"; + + std::string cmatrixKey = serialNumber.toStdString() + "/" + cmatrixName.toStdString(); + std::string spvectorKey = serialNumber.toStdString() + "/" + spvectorName.toStdString(); + + // Read h5 into table + DataSet datasetRead = fileRead.getDataSet(cmatrixKey); + auto cmatrixData = datasetRead.read<std::string>(); + Table cmatrixTable(cmatrixName, cmatrixData, ','); + + datasetRead = fileRead.getDataSet(spvectorKey); + auto spvectorData = datasetRead.read<std::string>(); + Table spvectorTable(spvectorName, spvectorData, ','); + + // Write bundle adjustment values out + cmatrixTable.Label().addComment(jigApplied); + c->write(cmatrixTable); + spvectorTable.Label().addComment(jigApplied); + c->write(spvectorTable); + + p.WriteHistory(*c); + } + + if (log) { + PvlGroup gp("JigsawResults"); + + gp += PvlKeyword("Status", "Bundle adjustment values from [" + ui.GetFileName("ADJUSTMENT_INPUT") + + "] were applied to the cubes in [" + cubeList+ "]"); + log->addLogGroup(gp); + } + + return; + } + } catch (IException &e) { + QString msg = "Unable to apply bundle adjustment values from [" + ui.GetFileName("ADJUSTMENT_INPUT") + "]"; + throw IException(e, IException::User, msg, _FILEINFO_); + } + // Check to make sure user entered something to adjust... Or can just points be in solution? // YES - we should be able to just TRIANGULATE the points in the control net // right now to do this we have to fake out jigsaw by @@ -59,7 +138,6 @@ namespace Isis { } QString cnetFile = ui.GetFileName("CNET"); - QString cubeList = ui.GetFileName("FROMLIST"); // retrieve settings from jigsaw gui BundleSettingsQsp settings = bundleSettings(ui); @@ -77,6 +155,7 @@ namespace Isis { } } settings->setCubeList(cubeList); + BundleAdjust *bundleAdjustment = NULL; try { // Get the held list if entered and prep for bundle adjustment @@ -130,24 +209,63 @@ namespace Isis { bundleSolution->outputResiduals(); } - // write lidar csv output file - if (ui.GetBoolean("LIDAR_CSV")) { - bundleSolution->outputLidarCSV(); - } + // write lidar csv output file + if (ui.GetBoolean("LIDAR_CSV")) { + bundleSolution->outputLidarCSV(); + } // write updated control net bundleAdjustment->controlNet()->Write(ui.GetFileName("ONET")); - // write updated lidar data file - if (ui.WasEntered("LIDARDATA")) { - if (ui.GetString("OLIDARFORMAT") == "JSON") { - bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Json); - } - else { - bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Binary); + // write updated lidar data file + if (ui.WasEntered("LIDARDATA")) { + if (ui.GetString("OLIDARFORMAT") == "JSON") { + bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Json); + } + else { + bundleAdjustment->lidarData()->write(ui.GetFileName("OLIDARDATA"),LidarData::Format::Binary); + } } - } + PvlGroup gp("JigsawResults"); + QString jigComment = "Jigged = " + Isis::iTime::CurrentLocalTime(); + + std::string outputFilePrefix = settings->outputFilePrefix().toStdString(); + + // ALWAYS* WRITE OUT ADJUSTMENT VALUES + // Do NOT write out for cubes w/ CSMState (TODO) + if (ui.GetBoolean("OUTADJUSTMENTH5")) { + std::string adjustmentFilename = outputFilePrefix + "adjustment_out.h5"; + + File file(adjustmentFilename, File::Truncate); + + for (int i = 0; i < bundleAdjustment->numberOfImages(); i++) { + Process p; + CubeAttributeInput inAtt; + Cube *c = p.SetInputCube(bundleAdjustment->fileName(i), inAtt, ReadWrite); + + // Only for ISIS adjustment values + if (!c->hasBlob("CSMState", "String")) { + Table cmatrix = bundleAdjustment->cMatrix(i); + Table spvector = bundleAdjustment->spVector(i); + + QString serialNumber = bundleAdjustment->serialNumberList()->serialNumber(i); + QString cmatrixName = cmatrix.Name(); + QString spvectorName = spvector.Name(); + + std::string cmatrixKey = serialNumber.toStdString() + "/" + cmatrixName.toStdString(); + std::string spvectorKey = serialNumber.toStdString() + "/" + spvectorName.toStdString(); + + // Save bundle adjustment values to HDF5 file + std::string cmatrixTableStr = Table::toString(cmatrix).toStdString(); + DataSet dataset = file.createDataSet<std::string>(cmatrixKey, cmatrixTableStr); + std::string spvectorTableStr = Table::toString(spvector).toStdString(); + dataset = file.createDataSet<std::string>(spvectorKey, spvectorTableStr); + } + } + file.flush(); + } + // Update the cube pointing if requested but ONLY if bundle has converged if (ui.GetBoolean("UPDATE") ) { if ( !bundleAdjustment->isConverged() ) { @@ -156,6 +274,8 @@ namespace Isis { throw IException(IException::Unknown, msg, _FILEINFO_); } else { + + // Loop through images for (int i = 0; i < bundleAdjustment->numberOfImages(); i++) { Process p; CubeAttributeInput inAtt; @@ -174,8 +294,7 @@ namespace Isis { break; } - // Update the image parameters - QString jigComment = "Jigged = " + Isis::iTime::CurrentLocalTime(); + // Only apply adjustment_input values for non-CSM for now if (c->hasBlob("CSMState", "String")) { Blob csmStateBlob("CSMState", "String"); // Read the BLOB from the cube to propagate things like the model @@ -185,8 +304,8 @@ namespace Isis { csmStateBlob.setData(modelState.c_str(), modelState.size()); csmStateBlob.Label().addComment(jigComment); c->write(csmStateBlob); - } - else { + } else { + // Write bundle adjustment values to cube Table cmatrix = bundleAdjustment->cMatrix(i); cmatrix.Label().addComment(jigComment); Table spvector = bundleAdjustment->spVector(i); @@ -194,6 +313,7 @@ namespace Isis { c->write(cmatrix); c->write(spvector); } + p.WriteHistory(*c); } gp += PvlKeyword("Status", "Camera pointing updated"); diff --git a/isis/src/control/apps/jigsaw/jigsaw.xml b/isis/src/control/apps/jigsaw/jigsaw.xml index 118c834b89209ae9067efd2718dcab989c8d283b..08abf0368811edb68878aa104d3324588b5a2e83 100644 --- a/isis/src/control/apps/jigsaw/jigsaw.xml +++ b/isis/src/control/apps/jigsaw/jigsaw.xml @@ -38,6 +38,19 @@ Updated camera information is only written to the <def>cube</def> labels if the bundle converges and the <b>UPDATE</b> parameter is selected. </p> + <p> + In order to apply bundle values using <b>ADJUSTMENT_INPUT</b>, only the option <b>FROMLIST</b> + must be set. If the <b>ADJUSTMENT_INPUT</b> is set to an HDF5 file containing bundle adjustment values + (usually the HDF5 output file from <b>OUTADJUSTMENTH5</b>), those values can be applied to the + <def>cube</def> labels. The adjustment file entails a group of HDF5 datasets where each dataset + contains the instrument pointing and position for each <def>cube</def>. The dataset key is composed + of the <def>cube</def> serial number and the table name, either "InstrumentPointing" or + "InstrumentPosition", separated by a forward slash "/". For example: + </p> + <pre> + SerialNumber: MRO/CTX/1016816309:030 + Dataset key for instrument pointing: MRO/CTX/1016816309:030/InstrumentPointing + </pre> <p> The input control net may be created by finding each image footprint with <b>footprintinit</b>, @@ -53,7 +66,10 @@ file with the <b>IMAGESCSV</b> option and likewise for the point statistics with the <b>OUTPUT_CSV</b> option. <b>RESIDUALS_CSV</b> provides a table of the measured image coordinates, the final <def>sample</def>, <def>line</def>, and overall residuals - of every control measure in both millimeters and pixels. + of every control measure in both millimeters and pixels. <b>OUTADJUSTMENTH5</b> stores bundle + adjustment values in an HDF5 file. Currently, only ISIS adjustment values are saved to file so + if all the cubes in the <b>FROMLIST</b> contain a CSM state blob then the <i>adjustment_out.h5</i> + file will be created but empty. </p> <h3>Observation Equations</h3> @@ -711,6 +727,9 @@ <b>autoseed</b> or <b>qnet</b>. It contains the <def>control point</def>s and associated measures. </description> + <exclusions> + <item>ADJUSTMENT_INPUT</item> + </exclusions> <filter> *.net </filter> @@ -727,6 +746,9 @@ the final coordinates of the <def>control point</def>s and residuals for each measurement. </description> + <exclusions> + <item>ADJUSTMENT_INPUT</item> + </exclusions> <filter> *.net </filter> @@ -875,6 +897,9 @@ POINT_RADIUS_SIGMA </item> </inclusions> + <exclusions> + <item>ADJUSTMENT_INPUT</item> + </exclusions> </parameter> <parameter name="UPDATE"> @@ -891,6 +916,9 @@ <default> <item>No</item> </default> + <exclusions> + <item>ADJUSTMENT_INPUT</item> + </exclusions> </parameter> <parameter name="OUTLIER_REJECTION"> @@ -1283,6 +1311,7 @@ <item>Yes</item> </default> </parameter> + <parameter name="OVEREXISTING"> <brief> Fit polynomial over the existing pointing</brief> <description> @@ -1769,7 +1798,7 @@ <brief>Output file prefix</brief> <description> File prefix to prepend for the generated output files. Any prefix that is not a - file path will have an underscore placed between the prefix and file name. + file path will have an underscore placed between the prefix and file name. </description> </parameter> @@ -1835,6 +1864,91 @@ <item>no</item> </default> </parameter> + + <parameter name="OUTADJUSTMENTH5"> + <brief> Outputs bundle adjustment values to HDF5 file - adjustment_out.h5</brief> + <description> + Selection of this parameter flags output of bundle adjustment + values to an HDF5 file. The HDF5 file may be empty if all the cubes + have CSM states. + </description> + <type>boolean</type> + <default> + <item>yes</item> + </default> + </parameter> + + <parameter name="ADJUSTMENT_INPUT"> + <type>filename</type> + <internalDefault>none</internalDefault> + <fileMode>input</fileMode> + <brief> + Reads in and applies adjustment values + </brief> + <description> + Reads in and applies bundle adjustment values from an HDF5 file, + usually the outputted adjustment_out.h5 file. + </description> + <filter> + *.h5 + </filter> + <exclusions> + <item>ONET</item> + <item>CNET</item> + <item>LIDARDATA</item> + <item>OLIDARDATA</item> + <item>OLIDARFORMAT</item> + <item>SCCONFIG</item> + <item>OBSERVATIONS</item> + <item>RADIUS</item> + <item>UPDATE</item> + <item>OUTLIER_REJECTION</item> + <item>REJECTION_MULTIPLIER</item> + <item>ERRORPROPAGATION</item> + <item>MODEL1</item> + <item>MAX_MODEL1_C_QUANTILE</item> + <item>MODEL2</item> + <item>MAX_MODEL2_C_QUANTILE</item> + <item>MODEL3</item> + <item>MAX_MODEL3_C_QUANTILE</item> + <item>SIGMA0</item> + <item>MAXITS</item> + <item>CKDEGREE</item> + <item>CKSOLVEDEGREE</item> + <item>CAMSOLVE</item> + <!-- <item>TWIST</item> --> + <item>OVEREXISTING</item> + <item>SPKDEGREE</item> + <item>SPKSOLVEDEGREE</item> + <item>SPSOLVE</item> + <item>OVERHERMITE</item> + <item>CSMSOLVESET</item> + <item>CSMSOLVETYPE</item> + <item>CSMSOLVELIST</item> + <item>SOLVETARGETBODY</item> + <item>TBPARAMETERS</item> + <item>CONTROL_POINT_COORDINATE_TYPE_BUNDLE</item> + <item>CONTROL_POINT_COORDINATE_TYPE_REPORTS</item> + <item>POINT_LATITUDE_SIGMA</item> + <item>POINT_LONGITUDE_SIGMA</item> + <item>POINT_RADIUS_SIGMA</item> + <item>POINT_X_SIGMA</item> + <item>POINT_Y_SIGMA</item> + <item>POINT_Z_SIGMA</item> + <item>SPACECRAFT_POSITION_SIGMA</item> + <item>SPACECRAFT_VELOCITY_SIGMA</item> + <item>SPACECRAFT_ACCELERATION_SIGMA</item> + <item>CAMERA_ANGLES_SIGMA</item> + <item>CAMERA_ANGULAR_VELOCITY_SIGMA</item> + <item>CAMERA_ANGULAR_ACCELERATION_SIGMA</item> + <!-- <item>BUNDLEOUT_TXT</item> + <item>IMAGESCSV</item> + <item>OUTPUT_CSV</item> + <item>RESIDUALS_CSV</item> + <item>LIDAR_CSV</item> + <item>OUTADJUSTMENTH5</item> --> + </exclusions> + </parameter> </group> </groups> diff --git a/isis/tests/FunctionalTestsJigsaw.cpp b/isis/tests/FunctionalTestsJigsaw.cpp index 3abcc3cfe21d879ffe661fc8a4b20a78d4e47949..61d957a41ccc7c08b7982b2d428828cad82a7394 100644 --- a/isis/tests/FunctionalTestsJigsaw.cpp +++ b/isis/tests/FunctionalTestsJigsaw.cpp @@ -15,6 +15,11 @@ #include "CSMCamera.h" #include "LidarData.h" #include "SerialNumber.h" +#include "BundleAdjust.h" +#include "BundleSettings.h" +#include <highfive/H5DataSet.hpp> +#include <highfive/H5File.hpp> + #include "jigsaw.h" @@ -1867,3 +1872,47 @@ TEST_F(LidarNetwork, FunctionalTestJigsawLidar) { } } + +TEST_F(ApolloNetwork, FunctionalTestJigsawSaveApplyValues) { + QVector<QString> args = {"spsolve=position", + "update=yes", + "bundleout_txt=no", + "cnet="+controlNetPath, + "fromlist="+tempDir.path() + "/cubes.lis", + "onet="+tempDir.path()+"/apollo_out.net", + "file_prefix="+tempDir.path()+"/"}; + + UserInterface ui(APP_XML, args); + + jigsaw(ui); + + // Check apollo_jigsaw.h5 was created + QString bundleOutput = tempDir.path()+"/adjustment_out.h5"; + HighFive::File file(bundleOutput.toStdString(), HighFive::File::ReadWrite); + + std::string datasetName = "/APOLLO15/METRIC/1971-08-01T15:37:39.428"; + QString cmatrixName = "InstrumentPointing"; + QString spvectorName = "InstrumentPosition"; + std::string cmatrixKey = datasetName + "/" + cmatrixName.toStdString(); + std::string spvectorKey = datasetName + "/" + spvectorName.toStdString(); + + HighFive::DataSet datasetRead = file.getDataSet(cmatrixKey); + auto cmatrixData = datasetRead.read<std::string>(); + Table cmatrixTable(cmatrixName, cmatrixData, ','); + std::string cmatrixTableStr = Table::toString(cmatrixTable).toStdString(); + + datasetRead = file.getDataSet(spvectorKey); + auto spvectorData = datasetRead.read<std::string>(); + Table spvectorTable(spvectorName, spvectorData, ','); + std::string spvectorTableStr = Table::toString(spvectorTable).toStdString(); + + EXPECT_EQ(cmatrixTable.RecordFields(), 8); + EXPECT_EQ(spvectorTable.RecordFields(), 7); + + EXPECT_EQ(cmatrixTableStr, + "J2000Q0,J2000Q1,J2000Q2,J2000Q3,AV1,AV2,AV3,ET\n0.72889620121855,0.66172757646101,-0.1261913882606,0.12207651669777,4.29360266307594e-04,6.9419874212449e-04,-6.23609851587137e-04,-896818899.38874\n"); + EXPECT_EQ(spvectorTableStr, + "J2000X,J2000Y,J2000Z,J2000XV,J2000YV,J2000ZV,ET\n491.19844009026,1198.1045282857,1313.7703671439,1.5198029518433,-0.58925196165899,-0.046463883259045,-896818899.38874\n"); + + file.flush(); +} diff --git a/isis/tests/TableTests.cpp b/isis/tests/TableTests.cpp index b8da823ec9f5a4c4ab77b191b73f73f52575e4e6..ee00d2551507f4942d1498974e5a71363700ed64 100644 --- a/isis/tests/TableTests.cpp +++ b/isis/tests/TableTests.cpp @@ -295,3 +295,33 @@ TEST(TableTests, Clear) { EXPECT_EQ(t.Records(), 0); } + +TEST(TableTests, FromString) { + std::string tableStr = "J2000Ang1,J2000Ang2,J2000Ang3\n" + "-1.0261086365746,1.3843980236775,0.97666760713915\n" + "-0.026127047776247,0.034245411189199,0.0052635095732964\n" + "-0.005717949450684,-0.0039014897927048,2.3750859084069e-05\n" + "260093852.48957,46.12915199995,2.0\n"; + + std::cout << "tableStr=" << tableStr << std::endl; + + QString tableName = QString::fromStdString("TestTableName"); + + std::stringstream tableStrStream; + tableStrStream << tableStr; + + Table table(tableName, tableStr, ','); + std::cout << "Created table with table string" << std::endl; + std::cout << "Table name=" << table.Name().toStdString() << std::endl; + std::cout << "Table recordFields=" << static_cast<int>(table.RecordFields()) << std::endl; + std::cout << "Table recordSize=" << static_cast<int>(table.RecordSize()) << std::endl; + + for (int i = 0; i < table.Records(); i++) { + std::cout << "Table[" << i << "] record=" << TableRecord::toString(table[i]).toStdString() << std::endl; + } + + QString tableToString = Table::toString(table); + std::cout << "tableToString=" << tableToString.toStdString() << std::endl; + + EXPECT_EQ(tableStr, tableToString.toStdString()); +}