diff --git a/UnitTest/sicd.cpp b/UnitTest/sicd.cpp index 6a141b7e1..e0b11986b 100644 --- a/UnitTest/sicd.cpp +++ b/UnitTest/sicd.cpp @@ -79,4 +79,8 @@ TEST_CLASS(test_valid_six) { public: #include "six/modules/c++/six.sicd/unittests/test_valid_six.cpp" }; -} \ No newline at end of file +TEST_CLASS(test_valid_sixsicd) { public: +#include "six/modules/c++/six.sicd/unittests/test_valid_sixsicd.cpp" +}; + +} diff --git a/six/modules/c++/cphd/include/cphd/Types.h b/six/modules/c++/cphd/include/cphd/Types.h index 7dabacced..51a53c1d4 100644 --- a/six/modules/c++/cphd/include/cphd/Types.h +++ b/six/modules/c++/cphd/include/cphd/Types.h @@ -94,9 +94,9 @@ typedef six::MatchInformation MatchInformation; enum class Version { - v100, // {"1.0.0", xml::lite::Uri("urn:CPHD:1.0.0")}, - v101, // {"1.0.1", xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.0.1")}, - v110, // {"1.1.0", xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.1.0")} + v1_0_0, // {"1.0.0", xml::lite::Uri("urn:CPHD:1.0.0")}, + v1_0_1, // {"1.0.1", xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.0.1")}, + v1_1_0, // {"1.1.0", xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.1.0")} }; std::string to_string(Version); // "1.0.0", "1.0.1", "1.1.0" diff --git a/six/modules/c++/cphd/source/CPHDXMLControl.cpp b/six/modules/c++/cphd/source/CPHDXMLControl.cpp index c25350aa8..a6cc2628e 100644 --- a/six/modules/c++/cphd/source/CPHDXMLControl.cpp +++ b/six/modules/c++/cphd/source/CPHDXMLControl.cpp @@ -124,9 +124,9 @@ std::unordered_map CPHDXMLControl::getVersionUriMap static std::map getVersionUriMap_() { static const std::map retval = { - {Version::v100, xml::lite::Uri("urn:CPHD:1.0.0")}, - {Version::v101, xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.0.1")}, - {Version::v110, xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.1.0")} + {Version::v1_0_0, xml::lite::Uri("urn:CPHD:1.0.0")}, + {Version::v1_0_1, xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.0.1")}, + {Version::v1_1_0, xml::lite::Uri("http://api.nsgreg.nga.mil/schema/cphd/1.1.0")} }; return retval; } @@ -231,9 +231,9 @@ std::string cphd::to_string(Version siddVersion) // Match existing version strings, see CPHDXMLControl::getVersionUriMap switch (siddVersion) { - case Version::v100: return "1.0.0"; - case Version::v101: return "1.0.1"; - case Version::v110: return "1.1.0"; + case Version::v1_0_0: return "1.0.0"; + case Version::v1_0_1: return "1.0.1"; + case Version::v1_1_0: return "1.1.0"; default: break; } throw std::logic_error("Unkown 'Version' value."); diff --git a/six/modules/c++/cphd/source/FileHeader.cpp b/six/modules/c++/cphd/source/FileHeader.cpp index bf7b71689..f818aaec1 100644 --- a/six/modules/c++/cphd/source/FileHeader.cpp +++ b/six/modules/c++/cphd/source/FileHeader.cpp @@ -34,7 +34,7 @@ namespace cphd static const char* getDefaultVersion() { - static const auto defaultVersion = to_string(Version::v101); + static const auto defaultVersion = to_string(Version::v1_0_1); return defaultVersion.c_str(); } const char* FileHeader::DEFAULT_VERSION = getDefaultVersion(); diff --git a/six/modules/c++/cphd/source/Metadata.cpp b/six/modules/c++/cphd/source/Metadata.cpp index 8b79b5835..542f6407b 100644 --- a/six/modules/c++/cphd/source/Metadata.cpp +++ b/six/modules/c++/cphd/source/Metadata.cpp @@ -80,15 +80,15 @@ void Metadata::setVersion(const std::string& version) { if (version == "1.0.0") { - setVersion(Version::v100); + setVersion(Version::v1_0_0); } else if (version == "1.0.1") { - setVersion(Version::v101); + setVersion(Version::v1_0_1); } else if (version == "1.1.0") { - setVersion(Version::v110); + setVersion(Version::v1_1_0); } else { diff --git a/six/modules/c++/six.sicd/unittests/test_valid_sixsicd.cpp b/six/modules/c++/six.sicd/unittests/test_valid_sixsicd.cpp index f7a64b555..b83d7c6aa 100644 --- a/six/modules/c++/six.sicd/unittests/test_valid_sixsicd.cpp +++ b/six/modules/c++/six.sicd/unittests/test_valid_sixsicd.cpp @@ -23,7 +23,6 @@ #include -#include #include #include #include @@ -37,26 +36,27 @@ #include "TestCase.h" -namespace fs = std::filesystem; - static std::unique_ptr test_assert_round_trip(const std::string& testName, - const six::sicd::ComplexData& complexData, const std::vector* pSchemaPaths) + const six::sicd::ComplexData& complexData, const std::vector* pSchemaPaths) { auto strXML = six::sicd::Utilities::toXMLString(complexData, pSchemaPaths); TEST_ASSERT_FALSE(strXML.empty()); return six::sicd::Utilities::parseDataFromString(strXML, pSchemaPaths); } -inline static const six::UnmodeledS* get_Unmodeled(const six::sicd::ComplexData& complexData, const std::string& strVersion) +inline static const six::Unmodeled* get_Unmodeled(const six::sicd::ComplexData& complexData, const std::string& strVersion) { if (strVersion != "1.3.0") // Unmodeled added in SICD 1.3 { return nullptr; } - else + + if (has_value(complexData.errorStatistics->unmodeled)) { - return complexData.errorStatistics->Unmodeled.get(); + return &value(complexData.errorStatistics->unmodeled); } + + return nullptr; } static void test_createFakeComplexData_(const std::string& testName, const std::string& strVersion) @@ -83,18 +83,18 @@ TEST_CASE(test_createFakeComplexData) test_createFakeComplexData_(testName, "1.3.0"); } -static void test_assert_unmodeled_(const std::string& testName, const six::UnmodeledS& Unmodeled) +static void test_assert_unmodeled_(const std::string& testName, const six::Unmodeled& unmodeled) { - TEST_ASSERT_EQ(1.23, Unmodeled.Xrow); - TEST_ASSERT_EQ(4.56, Unmodeled.Ycol); - TEST_ASSERT_EQ(7.89, Unmodeled.XrowYcol); - - const auto& unmodeledDecor = Unmodeled.unmodeledDecorr; - TEST_ASSERT(has_value(unmodeledDecor)); - TEST_ASSERT_EQ(12.34, value(unmodeledDecor).Xrow.CorrCoefZero); - TEST_ASSERT_EQ(56.78, value(unmodeledDecor).Xrow.DecorrRate); - TEST_ASSERT_EQ(123.4, value(unmodeledDecor).Ycol.CorrCoefZero); - TEST_ASSERT_EQ(567.8, value(unmodeledDecor).Ycol.DecorrRate); + TEST_ASSERT_EQ(1.23, unmodeled.Xrow); + TEST_ASSERT_EQ(4.56, unmodeled.Ycol); + TEST_ASSERT_EQ(7.89, unmodeled.XrowYcol); + + TEST_ASSERT(has_value(unmodeled.unmodeledDecorr)); + auto&& unmodeledDecor = value(unmodeled.unmodeledDecorr); + TEST_ASSERT_EQ(12.34, value(unmodeledDecor.Xrow).corrCoefZero); + TEST_ASSERT_EQ(56.78, value(unmodeledDecor.Xrow).decorrRate); + TEST_ASSERT_EQ(123.4, value(unmodeledDecor.Ycol).corrCoefZero); + TEST_ASSERT_EQ(567.8, value(unmodeledDecor.Ycol).decorrRate); } static void test_assert(const std::string& testName, const six::sicd::ComplexData& complexData) { @@ -105,9 +105,9 @@ static void test_assert(const std::string& testName, const six::sicd::ComplexDat return; } TEST_ASSERT(errorStatistics.get() != nullptr); - auto Unmodeled = errorStatistics->Unmodeled; - TEST_ASSERT(Unmodeled.get() != nullptr); - test_assert_unmodeled_(testName, *Unmodeled); + auto&& unmodeled = errorStatistics->unmodeled; + TEST_ASSERT(has_value(unmodeled)); + test_assert_unmodeled_(testName, value(unmodeled)); // for SICD 1.3, also check the polarization type; this is set either in the fake data or scid130.xml const auto txRcvPolarizationProc = complexData.imageFormation->txRcvPolarizationProc; @@ -116,9 +116,9 @@ static void test_assert(const std::string& testName, const six::sicd::ComplexDat TEST_ASSERT_EQ(strTxRcvPolarizationProc,"OTHER_TxRcvPolarizationProc:OTHER_TxRcvPolarizationProc"); } -static void test_read_sicd_xml(const std::string& testName, const fs::path& path) +static void test_read_sicd_xml(const std::string& testName, const std::filesystem::path& path) { - const auto pathname = six::testing::getSampleXmlPath(fs::path("six.sicd") / "tests" / "sample_xml", path); + const auto pathname = six::testing::getSampleXmlPath(std::filesystem::path("six.sicd") / "tests" / "sample_xml", path); // NULL schemaPaths, no validation auto pComplexData = six::sicd::Utilities::parseDataFromFile(pathname, nullptr /*pSchemaPaths*/); @@ -149,5 +149,5 @@ TEST_CASE(test_read_sicd130_xml) TEST_MAIN( TEST_CHECK(test_createFakeComplexData); TEST_CHECK(test_read_sicd110_xml); - TEST_CHECK(test_read_sicd110_xml); + TEST_CHECK(test_read_sicd130_xml); ) diff --git a/six/modules/c++/six.sidd/include/six/sidd/DerivedData.h b/six/modules/c++/six.sidd/include/six/sidd/DerivedData.h index 4eee55263..ed4109a1a 100644 --- a/six/modules/c++/six.sidd/include/six/sidd/DerivedData.h +++ b/six/modules/c++/six.sidd/include/six/sidd/DerivedData.h @@ -73,9 +73,9 @@ namespace sidd // SIDD 3.0.0 enum class Version { - v100, - v200, - v300, + v1_0_0, + v2_0_0, + v3_0_0, }; std::string to_string(Version); // "1.0.0", "2.0.0", "3.0.0" Version normalizeVersion(const std::string&); @@ -377,7 +377,7 @@ struct DerivedData : public Data static const char VENDOR_ID[]; bool equalTo(const Data& rhs) const override; - Version mVersion = Version::v100; // existing code + Version mVersion = Version::v1_0_0; // existing code six::sidd300::ISMVersion mISMVersion = six::sidd300::ISMVersion::current; // only for SIDD 3.0.0 }; } diff --git a/six/modules/c++/six.sidd/source/DerivedData.cpp b/six/modules/c++/six.sidd/source/DerivedData.cpp index 9e2930f35..7d07731b5 100644 --- a/six/modules/c++/six.sidd/source/DerivedData.cpp +++ b/six/modules/c++/six.sidd/source/DerivedData.cpp @@ -37,12 +37,12 @@ DerivedData::DerivedData(Version siddVersion, six::sidd300::ISMVersion ismVersio } DerivedData::DerivedData(Version siddVersion) : DerivedData(siddVersion, six::sidd300::get(six::sidd300::ISMVersion::current)) { - if (siddVersion == Version::v300) + if (siddVersion == Version::v3_0_0) { throw std::invalid_argument("Must use ISMVersion overload."); } } -DerivedData::DerivedData() : DerivedData(Version::v100) // existing code +DerivedData::DerivedData() : DerivedData(Version::v1_0_0) // existing code { } @@ -99,7 +99,7 @@ DateTime DerivedData::getCollectionStartDateTime() const const mem::ScopedCopyablePtr& DerivedData::getDisplayLUT() const { - if (mVersion == Version::v100) + if (mVersion == Version::v1_0_0) { if (display->remapInformation.get() == nullptr) { @@ -107,7 +107,7 @@ const mem::ScopedCopyablePtr& DerivedData::getDisplayLUT() const } return display->remapInformation->remapLUT; } - else if ((mVersion == Version::v200) || (mVersion == Version::v300)) + else if ((mVersion == Version::v2_0_0) || (mVersion == Version::v3_0_0)) { return nitfLUT; } @@ -191,7 +191,7 @@ void DerivedData::setSIDDVersion(Version siddVersion, six::sidd300::ISMVersion i } void DerivedData::setSIDDVersion(Version siddVersion) { - if (siddVersion == Version::v300) + if (siddVersion == Version::v3_0_0) { throw std::invalid_argument("Must use ISMVersion overload."); // TODO } diff --git a/six/modules/c++/six.sidd/source/DerivedXMLControl.cpp b/six/modules/c++/six.sidd/source/DerivedXMLControl.cpp index d6053ee70..1d843a620 100644 --- a/six/modules/c++/six.sidd/source/DerivedXMLControl.cpp +++ b/six/modules/c++/six.sidd/source/DerivedXMLControl.cpp @@ -62,15 +62,15 @@ six::sidd::Version six::sidd::normalizeVersion(const std::string& strVersion) // SIDD 3.0.0 if (normalizedVersion == "100") { - return six::sidd::Version::v100; + return six::sidd::Version::v1_0_0; } if (normalizedVersion == "200") { - return six::sidd::Version::v200; + return six::sidd::Version::v2_0_0; } if (normalizedVersion == "300") { - return six::sidd::Version::v300; + return six::sidd::Version::v3_0_0; } if (normalizedVersion == "110") @@ -92,9 +92,9 @@ namespace sidd // Match "incoming" SIDD version strings; this is also what the XML expects, see normalizeVersion() switch (siddVersion) { - case Version::v100: return "1.0.0"; - case Version::v200: return "2.0.0"; - case Version::v300: return "3.0.0"; + case Version::v1_0_0: return "1.0.0"; + case Version::v2_0_0: return "2.0.0"; + case Version::v3_0_0: return "3.0.0"; default: break; } throw std::logic_error("Unkown 'Version' value."); @@ -330,15 +330,15 @@ DerivedXMLControl::getParser(Version normalizedVersion, std::optional(mLog); } - if (normalizedVersion == Version::v200) + if (normalizedVersion == Version::v2_0_0) { return std::make_unique(mLog); } - if (normalizedVersion == Version::v300) + if (normalizedVersion == Version::v3_0_0) { if (!ismVersion.has_value()) { diff --git a/six/modules/c++/six.sidd/source/DerivedXMLParser100.cpp b/six/modules/c++/six.sidd/source/DerivedXMLParser100.cpp index d9f5e4c6f..afcd9aef9 100644 --- a/six/modules/c++/six.sidd/source/DerivedXMLParser100.cpp +++ b/six/modules/c++/six.sidd/source/DerivedXMLParser100.cpp @@ -34,7 +34,7 @@ namespace six { namespace sidd { -constexpr Version VERSION = Version::v100; +constexpr Version VERSION = Version::v1_0_0; const char SI_COMMON_URI[] = "urn:SICommon:0.1"; const char ISM_URI[] = "urn:us:gov:ic:ism"; inline static xml::lite::Uri getISMUri() diff --git a/six/modules/c++/six.sidd/source/DerivedXMLParser200.cpp b/six/modules/c++/six.sidd/source/DerivedXMLParser200.cpp index 9228dabd7..e2b0fdc7e 100644 --- a/six/modules/c++/six.sidd/source/DerivedXMLParser200.cpp +++ b/six/modules/c++/six.sidd/source/DerivedXMLParser200.cpp @@ -113,7 +113,7 @@ ProjectionType DerivedXMLParser200::getProjectionType(const xml::lite::Element& } -constexpr Version VERSION = Version::v200; +constexpr Version VERSION = Version::v2_0_0; const char SI_COMMON_URI[] = "urn:SICommon:1.0"; inline static xml::lite::Uri getISMUri() { diff --git a/six/modules/c++/six.sidd/source/DerivedXMLParser300.cpp b/six/modules/c++/six.sidd/source/DerivedXMLParser300.cpp index 7392a50d5..c568e410e 100644 --- a/six/modules/c++/six.sidd/source/DerivedXMLParser300.cpp +++ b/six/modules/c++/six.sidd/source/DerivedXMLParser300.cpp @@ -39,7 +39,7 @@ namespace six { namespace sidd { -constexpr Version VERSION = Version::v300; +constexpr Version VERSION = Version::v3_0_0; static const char SI_COMMON_URI[] = "urn:SICommon:1.0"; // There is a need to support two different versions of ISM with SIDD 3.0 :-( diff --git a/six/modules/c++/six.sidd/source/Utilities.cpp b/six/modules/c++/six.sidd/source/Utilities.cpp index c723af2db..e4f4f1474 100644 --- a/six/modules/c++/six.sidd/source/Utilities.cpp +++ b/six/modules/c++/six.sidd/source/Utilities.cpp @@ -690,7 +690,7 @@ static void initProductCreation(six::sidd::ProductCreation& productCreation, Ver productCreation.productCreationExtensions.push_back(parameter); productCreation.classification.securityExtensions.push_back(parameter); - if (siddVersion != Version::v300) + if (siddVersion != Version::v3_0_0) { productCreation.classification.desVersion = 234; // existing code } @@ -701,7 +701,7 @@ static void initProductCreation(six::sidd::ProductCreation& productCreation, Ver productCreation.classification.createDate = six::DateTime(); productCreation.classification.classification = "U"; - if (siddVersion == Version::v100) + if (siddVersion == Version::v1_0_0) { productCreation.classification.compliesWith.push_back("ICD-710"); } @@ -939,7 +939,7 @@ static void initExploitationFeatures(six::sidd::ExploitationFeatures& exFeatures polarization->txPolarization = six::PolarizationSequenceType::V; polarization->rcvPolarization = six::PolarizationSequenceType::OTHER; polarization->rcvPolarizationOffset = 1.37; - if (siddVersion == Version::v100) + if (siddVersion == Version::v1_0_0) { polarization->processed = six::BooleanType::IS_TRUE; } @@ -957,7 +957,7 @@ static void initExploitationFeatures(six::sidd::ExploitationFeatures& exFeatures collection.geometry->extensions.push_back(param); collection.phenomenology.reset(new six::sidd::Phenomenology()); - if (siddVersion != Version::v300) + if (siddVersion != Version::v3_0_0) { // [-180, 180) before SIDD 3.0 collection.phenomenology->shadow = six::AngleMagnitude(-1.5, 3.7); @@ -979,7 +979,7 @@ static void initExploitationFeatures(six::sidd::ExploitationFeatures& exFeatures exFeatures.product[0].north = 58.332; exFeatures.product[0].extensions.push_back(param); - if (siddVersion == Version::v200) + if (siddVersion == Version::v2_0_0) { exFeatures.product[0].ellipticity = 12.0; exFeatures.product[0].polarization.resize(1); @@ -1125,7 +1125,7 @@ static void populateData(six::sidd::DerivedData& siddData, const std::string& lu constexpr bool smallImage = true; const auto elementSize = static_cast(lutType == "Mono" ? 2 : 3); - if ((siddVersion == Version::v200) || (siddVersion == Version::v300)) + if ((siddVersion == Version::v2_0_0) || (siddVersion == Version::v3_0_0)) { // This will naturally get constructed in the course of 1.0.0 // Separate field in 2.0.0 @@ -1391,11 +1391,11 @@ static std::unique_ptr createFakeDerivedData_(const Version* pSiddV } std::unique_ptr Utilities::createFakeDerivedData(Version siddVersion) { - if (siddVersion == Version::v300) + if (siddVersion == Version::v3_0_0) { throw std::invalid_argument("Must use ISMVersion overload."); } - if (siddVersion == Version::v200) + if (siddVersion == Version::v2_0_0) { return createFakeDerivedData_(&siddVersion); } @@ -1403,7 +1403,7 @@ std::unique_ptr Utilities::createFakeDerivedData(Version siddVersio } std::unique_ptr Utilities::createFakeDerivedData(Version siddVersion, six::sidd300::ISMVersion ismVersion) { - if (siddVersion != Version::v300) + if (siddVersion != Version::v3_0_0) { return createFakeDerivedData(siddVersion); } diff --git a/six/modules/c++/six.sidd/tests/sample_nitf/2023-07-26-11-37-27_UMBRA-04_SIDD.nitf b/six/modules/c++/six.sidd/tests/sample_nitf/2023-07-26-11-37-27_UMBRA-04_SIDD.nitf new file mode 100644 index 000000000..a99c10572 Binary files /dev/null and b/six/modules/c++/six.sidd/tests/sample_nitf/2023-07-26-11-37-27_UMBRA-04_SIDD.nitf differ diff --git a/six/modules/c++/six.sidd/unittests/test_valid_sixsidd.cpp b/six/modules/c++/six.sidd/unittests/test_valid_sixsidd.cpp index c2f8fba81..34a4cf842 100644 --- a/six/modules/c++/six.sidd/unittests/test_valid_sixsidd.cpp +++ b/six/modules/c++/six.sidd/unittests/test_valid_sixsidd.cpp @@ -50,6 +50,11 @@ static std::filesystem::path schema_relative_path() return six_sidd_relative_path() / "conf" / "schema"; // .../conf/schema } +static std::filesystem::path get_sample_nitf_path(const std::filesystem::path& filename) +{ + static const auto modulePath = six_sidd_relative_path() / "tests" / "sample_nitf"; + return sys::test::findGITModuleFile("six", modulePath, filename); +} static std::filesystem::path get_sample_xml_path(const std::filesystem::path& filename) { static const auto modulePath = six_sidd_relative_path() / "tests" / "sample_xml"; @@ -75,16 +80,19 @@ static std::unique_ptr test_assert_round_trip(const std: return six::sidd::Utilities::parseDataFromString(strXML, pSchemaPaths); } -inline static const six::UnmodeledS* get_Unmodeled(const six::sidd::DerivedData& derivedData, six::sidd::Version siddVersion) +inline static const six::Unmodeled* get_Unmodeled(const six::sidd::DerivedData& derivedData, six::sidd::Version siddVersion) { - if (siddVersion != six::sidd::Version::v300) // Unmodeled added in SIDD 3.0 + if (siddVersion != six::sidd::Version::v3_0_0) // Unmodeled added in SIDD 3.0 { return nullptr; } - else + + if (has_value(derivedData.errorStatistics->unmodeled)) { - return derivedData.errorStatistics->Unmodeled.get(); + return &value(derivedData.errorStatistics->unmodeled); } + + return nullptr; } static void test_createFakeDerivedData_(const std::string& testName, bool validate, @@ -103,31 +111,44 @@ static void test_createFakeDerivedData_(const std::string& testName, bool valida } TEST_CASE(test_createFakeDerivedData) { - test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v200, six::sidd300::get(six::sidd300::ISMVersion::current)); + test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v2_0_0, six::sidd300::get(six::sidd300::ISMVersion::current)); - test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v300, six::sidd300::ISMVersion::v13); - test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v300, six::sidd300::ISMVersion::v201609); + test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v3_0_0, six::sidd300::ISMVersion::v13); + test_createFakeDerivedData_(testName, false /*validate*/, six::sidd::Version::v3_0_0, six::sidd300::ISMVersion::v201609); } TEST_CASE(test_createFakeDerivedData_validate) { - test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v200, six::sidd300::get(six::sidd300::ISMVersion::current)); + test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v2_0_0, six::sidd300::get(six::sidd300::ISMVersion::current)); + + test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v3_0_0, six::sidd300::ISMVersion::v13); + test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v3_0_0, six::sidd300::ISMVersion::v201609); +} + +TEST_CASE(test_read_sidd200_no_LUT) +{ + static const auto pathname = get_sample_nitf_path("2023-07-26-11-37-27_UMBRA-04_SIDD.nitf"); + + six::XMLControlRegistry& xml_registry = six::XMLControlFactory::getInstance(); + xml_registry.addCreator(six::DataType::DERIVED, + new six::XMLControlCreatorT()); - test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v300, six::sidd300::ISMVersion::v13); - test_createFakeDerivedData_(testName, true /*validate*/, six::sidd::Version::v300, six::sidd300::ISMVersion::v201609); + six::NITFReadControl reader; + reader.setXMLControlRegistry(xml_registry); + reader.load(pathname.string()); } -static void test_assert_unmodeled_(const std::string& testName, const six::UnmodeledS& Unmodeled) +static void test_assert_unmodeled_(const std::string& testName, const six::Unmodeled& unmodeled) { - TEST_ASSERT_EQ(1.23, Unmodeled.Xrow); - TEST_ASSERT_EQ(4.56, Unmodeled.Ycol); - TEST_ASSERT_EQ(7.89, Unmodeled.XrowYcol); + TEST_ASSERT_EQ(1.23, unmodeled.Xrow); + TEST_ASSERT_EQ(4.56, unmodeled.Ycol); + TEST_ASSERT_EQ(7.89, unmodeled.XrowYcol); - const auto& unmodeledDecor = Unmodeled.unmodeledDecorr; - TEST_ASSERT_TRUE(has_value(unmodeledDecor)); - TEST_ASSERT_EQ(12.34, value(unmodeledDecor).Xrow.CorrCoefZero); - TEST_ASSERT_EQ(56.78, value(unmodeledDecor).Xrow.DecorrRate); - TEST_ASSERT_EQ(123.4, value(unmodeledDecor).Ycol.CorrCoefZero); - TEST_ASSERT_EQ(567.8, value(unmodeledDecor).Ycol.DecorrRate); + TEST_ASSERT_TRUE(has_value(unmodeled.unmodeledDecorr)); + auto&& unmodeledDecor = value(unmodeled.unmodeledDecorr); + TEST_ASSERT_EQ(12.34, value(unmodeledDecor.Xrow).corrCoefZero); + TEST_ASSERT_EQ(56.78, value(unmodeledDecor.Xrow).decorrRate); + TEST_ASSERT_EQ(123.4, value(unmodeledDecor.Ycol).corrCoefZero); + TEST_ASSERT_EQ(567.8, value(unmodeledDecor.Ycol).decorrRate); } static void test_assert_unmodeled(const std::string& testName, const six::sidd::DerivedData& derivedData) { @@ -138,9 +159,9 @@ static void test_assert_unmodeled(const std::string& testName, const six::sidd:: return; } - auto Unmodeled = errorStatistics->Unmodeled; - TEST_ASSERT(Unmodeled.get() != nullptr); - test_assert_unmodeled_(testName, *Unmodeled); + auto unmodeled = errorStatistics->unmodeled; + TEST_ASSERT(has_value(unmodeled)); + test_assert_unmodeled_(testName, value(unmodeled)); } static void test_read_sidd_xml(const std::string& testName, const std::filesystem::path& path, @@ -180,6 +201,7 @@ TEST_CASE(test_read_sidd300_v13_xml) TEST_MAIN( TEST_CHECK(test_createFakeDerivedData); TEST_CHECK(test_createFakeDerivedData_validate); + TEST_CHECK(test_read_sidd200_no_LUT); TEST_CHECK(test_read_sidd200_xml); TEST_CHECK(test_read_sidd300_xml); TEST_CHECK(test_read_sidd300_v13_xml); diff --git a/six/modules/c++/six/include/six/ErrorStatistics.h b/six/modules/c++/six/include/six/ErrorStatistics.h index 86383dd1e..beda61a7d 100644 --- a/six/modules/c++/six/include/six/ErrorStatistics.h +++ b/six/modules/c++/six/include/six/ErrorStatistics.h @@ -255,7 +255,7 @@ struct IonoError * * Contains Unmodeled error statistics */ -struct UnmodeledS final +struct Unmodeled final { XsElement Xrow{ "Xrow" }; XsElement Ycol{ "Ycol" }; @@ -265,27 +265,31 @@ struct UnmodeledS final { struct Xrow_Ycol final { - double CorrCoefZero = 0.0; - double DecorrRate = 0.0; + XsElement corrCoefZero{ "CorrCoefZero" }; + XsElement decorrRate{ "DecorrRate" }; }; - Xrow_Ycol Xrow{}; - Xrow_Ycol Ycol{}; + XsElement Xrow{ "Xrow" }; + XsElement Ycol{ "Ycol" }; }; XsElement_minOccurs0 unmodeledDecorr{ "UnmodeledDecorr" }; }; -inline bool operator==(const UnmodeledS::Decorr& lhs, const UnmodeledS::Decorr& rhs) +inline bool operator==(const Unmodeled::Decorr& lhs, const Unmodeled::Decorr& rhs) { - return (lhs.Xrow.CorrCoefZero == rhs.Xrow.CorrCoefZero) - && (lhs.Xrow.DecorrRate == rhs.Xrow.DecorrRate) - && (lhs.Ycol.CorrCoefZero == rhs.Ycol.CorrCoefZero) - && (lhs.Ycol.DecorrRate == rhs.Ycol.DecorrRate) + auto&& lhs_Xrow = value(lhs.Xrow); + auto&& rhs_Xrow = value(rhs.Xrow); + auto&& lhs_Ycol = value(lhs.Ycol); + auto&& rhs_Ycol = value(rhs.Ycol); + return (lhs_Xrow.corrCoefZero == rhs_Xrow.corrCoefZero) + && (lhs_Xrow.decorrRate == rhs_Xrow.decorrRate) + && (lhs_Ycol.corrCoefZero == rhs_Ycol.corrCoefZero) + && (lhs_Ycol.decorrRate == rhs_Ycol.decorrRate) ; } -inline bool operator!=(const UnmodeledS::Decorr& lhs, const UnmodeledS::Decorr& rhs) +inline bool operator!=(const Unmodeled::Decorr& lhs, const Unmodeled::Decorr& rhs) { return !(lhs == rhs); } -inline bool operator==(const UnmodeledS& lhs, const UnmodeledS& rhs) +inline bool operator==(const Unmodeled& lhs, const Unmodeled& rhs) { return (lhs.Xrow == rhs.Xrow) && (lhs.Ycol == rhs.Ycol) @@ -293,7 +297,7 @@ inline bool operator==(const UnmodeledS& lhs, const UnmodeledS& rhs) && (lhs.unmodeledDecorr == rhs.unmodeledDecorr) ; } -inline bool operator!=(const UnmodeledS& lhs, const UnmodeledS& rhs) +inline bool operator!=(const Unmodeled& lhs, const Unmodeled& rhs) { return !(lhs == rhs); } @@ -406,7 +410,7 @@ struct ErrorStatistics * (Optional) Unmodeled * */ - mem::ScopedCopyablePtr Unmodeled; + XsElement_minOccurs0 unmodeled{ "Unmodeled" }; /*! * Additional parameters @@ -420,7 +424,7 @@ struct ErrorStatistics return (compositeSCP == rhs.compositeSCP) && (components == rhs.components) && (additionalParameters == rhs.additionalParameters) - && (Unmodeled == rhs.Unmodeled) + && (unmodeled == rhs.unmodeled) ; } bool operator!=(const ErrorStatistics& rhs) const diff --git a/six/modules/c++/six/include/six/NITFReadControl.h b/six/modules/c++/six/include/six/NITFReadControl.h index 0167ff7d1..e0dbf8c9e 100644 --- a/six/modules/c++/six/include/six/NITFReadControl.h +++ b/six/modules/c++/six/include/six/NITFReadControl.h @@ -278,6 +278,8 @@ struct NITFReadControl : public ReadControl return (iCat == "DED"); } + void setDisplayLUT(six::NITFImageInfo&, const nitf::ImageSubheader&); // LUT processing for SIDDs + // We need this for one of the load overloadings // to prevent data from being deleted prematurely // The issue occurs from the explicit destructor of diff --git a/six/modules/c++/six/include/six/ReadControl.h b/six/modules/c++/six/include/six/ReadControl.h index b516525c1..92e21d78e 100644 --- a/six/modules/c++/six/include/six/ReadControl.h +++ b/six/modules/c++/six/include/six/ReadControl.h @@ -210,6 +210,10 @@ struct ReadControl if (!mXMLRegistry) mXMLRegistry = &XMLControlFactory::getInstance(); } + void setXMLControlRegistry(const XMLControlRegistry& xmlRegistry) + { + setXMLControlRegistry(&xmlRegistry); + } protected: std::shared_ptr mContainer; diff --git a/six/modules/c++/six/include/six/SICommonXMLParser.h b/six/modules/c++/six/include/six/SICommonXMLParser.h index 4da3276e7..a33207981 100644 --- a/six/modules/c++/six/include/six/SICommonXMLParser.h +++ b/six/modules/c++/six/include/six/SICommonXMLParser.h @@ -224,6 +224,11 @@ struct SICommonXMLParser : public XMLParser // TODO: Can we combine this with parsePoly1D()? void parsePoly(const xml::lite::Element* polyXML, size_t xyzIdx, PolyXYZ& polyXYZ) const; + XMLElem createFromElement(const six::XsElement&, xml::lite::Element&) const; + + template + XMLElem newFromElement(const XsElement_minOccurs0& element, xml::lite::Element& parent) const; + private: const std::string mSICommonURI; }; diff --git a/six/modules/c++/six/include/six/XMLParser.h b/six/modules/c++/six/include/six/XMLParser.h index 647f55108..864da1243 100644 --- a/six/modules/c++/six/include/six/XMLParser.h +++ b/six/modules/c++/six/include/six/XMLParser.h @@ -311,6 +311,12 @@ struct XMLParser { return getOptional_reset(*parent, tag, obj); } + template + static XMLElem getOptional_reset(const xml::lite::Element& parent, XsElement_minOccurs0& obj) + { + return XmlLite::getOptional_reset(parent, obj); + } + static XMLElem getFirstAndOnly(const xml::lite::Element* parent, const std::string& tag); static xml::lite::Element& getFirstAndOnly(const xml::lite::Element& parent, const std::string& tag); diff --git a/six/modules/c++/six/include/six/XmlLite.h b/six/modules/c++/six/include/six/XmlLite.h index 033eaaf96..8c898f1d0 100644 --- a/six/modules/c++/six/include/six/XmlLite.h +++ b/six/modules/c++/six/include/six/XmlLite.h @@ -274,6 +274,16 @@ struct XmlLite final } return retval; } + template + inline static xml::lite::Element* getOptional_reset(const xml::lite::Element& parent, XsElement_minOccurs0& obj) + { + auto retval = getOptional(parent, obj.tag()); + if (retval != nullptr) + { + obj = T{}; + } + return retval; + } static xml::lite::Element& getFirstAndOnly(const xml::lite::Element& parent, const std::string& tag); static xml::lite::Element& getFirstAndOnly(const xml::lite::Element& parent, const xml::lite::QName&); diff --git a/six/modules/c++/six/include/six/XsElement.h b/six/modules/c++/six/include/six/XsElement.h index 660111396..26f3f7fa9 100644 --- a/six/modules/c++/six/include/six/XsElement.h +++ b/six/modules/c++/six/include/six/XsElement.h @@ -233,17 +233,5 @@ inline std::ostream& operator<<(std::ostream& os, const XsElement_minOccurs0& return os; } -// Existing code uses mem::ScopedCopyablePtr rather than std::optional -template -inline auto get(const XsElement_minOccurs0& o) -{ - return has_value(o) ? &(value(o)) : nullptr; -} -template -inline auto get(XsElement_minOccurs0& o) -{ - return has_value(o) ? &(value(o)) : nullptr; -} - } #endif // SIX_six_XsElement_h_INCLUDED_ diff --git a/six/modules/c++/six/source/NITFReadControl.cpp b/six/modules/c++/six/source/NITFReadControl.cpp index ae0fcc7a1..d40e95cd3 100644 --- a/six/modules/c++/six/source/NITFReadControl.cpp +++ b/six/modules/c++/six/source/NITFReadControl.cpp @@ -455,16 +455,41 @@ void NITFReadControl::load_(std::shared_ptr ioInterface, cons } } - // SIDD 2.0 needs to read LUT directly from NITF - const auto pData = currentInfo->getData(); - if (pData->getDataType() == DataType::DERIVED && pData->getVersion() == "2.0.0") + setDisplayLUT(*currentInfo, subheader); + + currentInfo->addSegment(si); + } +} +void NITFReadControl::setDisplayLUT(six::NITFImageInfo& currentInfo, const nitf::ImageSubheader& subheader) +{ + // SIDD 2.0 needs to read LUT directly from NITF + const auto pData = currentInfo.getData(); + if (pData->getDataType() != DataType::DERIVED) + { + // LUT processing only for SIDDs + return; + } + + const auto version = pData->getVersion(); + if (version == "1.0.0") // TODO: remove this hard-coded SIDD version check + { + // LUTs only with SIDD 2.0 and later + return; + } + + // There's no requirement for SIDD 2.0 to have a LUT + const auto bandInfo0 = subheader.getBandInfo(0); + const int numLUTs = bandInfo0.getNumLUTs(); + if (numLUTs > 0) // avoid getLookupTable() creating an empty nitf::LookupTable + { + const auto nitfLut = bandInfo0.getLookupTable(); + if (nitfLut.getEntries() > 0) { - const auto nitfLut = subheader.getBandInfo(0).getLookupTable(); - currentInfo->getData_()->setDisplayLUT(std::make_unique(nitfLut)); + currentInfo.getData_()->setDisplayLUT(std::make_unique(nitfLut)); } - currentInfo->addSegment(si); } } + void NITFReadControl::load(std::shared_ptr ioInterface, const std::vector* pSchemaPaths_) { const std::vector schemaPaths; diff --git a/six/modules/c++/six/source/SICommonXMLParser.cpp b/six/modules/c++/six/source/SICommonXMLParser.cpp index 3957d8d29..df319c8a1 100644 --- a/six/modules/c++/six/source/SICommonXMLParser.cpp +++ b/six/modules/c++/six/source/SICommonXMLParser.cpp @@ -824,6 +824,21 @@ void SICommonXMLParser::parseRowColInts( } } +XMLElem SICommonXMLParser::createFromElement(const six::XsElement& elem, xml::lite::Element& parent) const +{ + return createDouble(elem.tag(), getSICommonURI(), elem.value(), &parent); +} + +template +XMLElem SICommonXMLParser::newFromElement(const XsElement_minOccurs0& elem, xml::lite::Element& parent) const +{ + if (!has_value(elem)) + { + return nullptr; + } + return newElement(&value(elem), elem.tag(), getSICommonURI(), &parent); +} + XMLElem SICommonXMLParser::convertErrorStatisticsToXML( const ErrorStatistics* errorStatistics, XMLElem parent) const @@ -936,33 +951,27 @@ XMLElem SICommonXMLParser::convertErrorStatisticsToXML( } } - #define xmlNewElement(name, uri, xml) auto name##XML = newElement(name, #name, uri, xml) - const auto Unmodeled = errorStatistics->Unmodeled.get(); - xmlNewElement(Unmodeled, getSICommonURI(), errorStatsXML); - if (UnmodeledXML != nullptr) + if (auto unmodeledXML = newFromElement(errorStatistics->unmodeled, *errorStatsXML)) { - #define xmlCreateElement_(name, xml) createDouble((name).tag(), getSICommonURI(), (name).value(), xml) - #define xmlCreateElement(name, elem) xmlCreateElement_(elem->name, elem ## XML); - xmlCreateElement(Xrow, Unmodeled); - xmlCreateElement(Ycol, Unmodeled); - xmlCreateElement(XrowYcol, Unmodeled); - - const auto pUnmodeledDecorr = get(Unmodeled->unmodeledDecorr); - auto UnmodeledDecorrXML = newElement(pUnmodeledDecorr, "UnmodeledDecorr", getSICommonURI(), UnmodeledXML); - if (UnmodeledDecorrXML != nullptr) + const auto& unmodeled = value(errorStatistics->unmodeled); + createFromElement(unmodeled.Xrow, *unmodeledXML); + createFromElement(unmodeled.Ycol, *unmodeledXML); + createFromElement(unmodeled.XrowYcol, *unmodeledXML); + + if (auto unmodeledDecorrXML = newFromElement(unmodeled.unmodeledDecorr, *unmodeledXML)) { - #define xmlCreateDouble_(name, value, elem) createDouble(#name, getSICommonURI(), (value).name, elem) - #define xmlCreateDouble(name, elem) xmlCreateDouble_(name, *elem, elem ## XML) - - const auto& Xrow = &(pUnmodeledDecorr->Xrow); - xmlNewElement(Xrow, getSICommonURI(), UnmodeledDecorrXML); - xmlCreateDouble(CorrCoefZero, Xrow); - xmlCreateDouble(DecorrRate, Xrow); - - const auto& Ycol = &(pUnmodeledDecorr->Ycol); - xmlNewElement(Ycol, getSICommonURI(), UnmodeledDecorrXML); - xmlCreateDouble(CorrCoefZero, Ycol); - xmlCreateDouble(DecorrRate, Ycol); + const auto new_Xrow_Ycol = [&](const XsElement& elem) + { + if (auto pXML = newElement(&elem, elem.tag(), getSICommonURI(), unmodeledDecorrXML)) + { + createFromElement(value(elem).corrCoefZero, *pXML); + createFromElement(value(elem).decorrRate, *pXML); + } + }; + + const auto& unmodeledDecorr = value(unmodeled.unmodeledDecorr); + new_Xrow_Ycol(unmodeledDecorr.Xrow); + new_Xrow_Ycol(unmodeledDecorr.Ycol); } } @@ -979,12 +988,12 @@ XMLElem SICommonXMLParser::convertErrorStatisticsToXML( } inline static XMLElem getOptionalUnmodeledDecorr(const xml::lite::Element& parent, - XsElement_minOccurs0& unmodeledDecorr) + XsElement_minOccurs0& unmodeledDecorr) { auto retval = XmlLite::getOptional(parent, unmodeledDecorr.tag()); if (retval != nullptr) { - unmodeledDecorr = UnmodeledS::Decorr{}; + unmodeledDecorr = Unmodeled::Decorr{}; } return retval; } @@ -1039,58 +1048,29 @@ void SICommonXMLParser::parseErrorStatisticsFromXML( } } - #define xml_getOptional_reset(xml, pRoot, name) getOptional_reset(xml, #name, (pRoot).name); - tmpElem = xml_getOptional_reset(errorStatsXML, errorStatistics, Unmodeled); // SIDD 3.0 - if (tmpElem != nullptr) - { - auto& Unmodeled = *(errorStatistics.Unmodeled); - - // macro to avoid copy/paste errors - #define parseDouble_getFirstAndOnly(elem, name, storage) parseDouble(getFirstAndOnly(elem, #name), (storage).name) - - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.Xrow); - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.Ycol); - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.XrowYcol); - - auto unmodeledDecorrXML = getOptionalUnmodeledDecorr(*tmpElem, Unmodeled.unmodeledDecorr); - if (unmodeledDecorrXML != nullptr) - { - auto& UnmodeledDecorr = *get(errorStatistics.Unmodeled->unmodeledDecorr); - - auto xrowXML = getFirstAndOnly(unmodeledDecorrXML, "Xrow"); - parseDouble_getFirstAndOnly(xrowXML, CorrCoefZero, UnmodeledDecorr.Xrow); - parseDouble_getFirstAndOnly(xrowXML, DecorrRate, UnmodeledDecorr.Xrow); - - auto ycolXML = getFirstAndOnly(unmodeledDecorrXML, "Ycol"); - parseDouble_getFirstAndOnly(ycolXML, CorrCoefZero, UnmodeledDecorr.Ycol); - parseDouble_getFirstAndOnly(ycolXML, DecorrRate, UnmodeledDecorr.Ycol); - } - } - - tmpElem = xml_getOptional_reset(errorStatsXML, errorStatistics, Unmodeled); // SIDD 3.0 - if (tmpElem != nullptr) + if (const auto pUnmodeledXML = getOptional_reset(errorStatsXML, (errorStatistics).unmodeled)) // SIDD 3.0 { - auto& Unmodeled = *(errorStatistics.Unmodeled); - - // macro to avoid copy/paste errors - #define parseDouble_getFirstAndOnly(elem, name, storage) parseDouble(getFirstAndOnly(elem, #name), (storage).name) - - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.Xrow); - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.Ycol); - six::getFirstAndOnly(parser(), *tmpElem, Unmodeled.XrowYcol); - - auto unmodeledDecorrXML = getOptionalUnmodeledDecorr(*tmpElem, Unmodeled.unmodeledDecorr); - if (unmodeledDecorrXML != nullptr) + auto&& unmodeledXML = *pUnmodeledXML; + + auto& unmodeled = value(errorStatistics.unmodeled); + six::getFirstAndOnly(parser(), unmodeledXML, unmodeled.Xrow); + six::getFirstAndOnly(parser(), unmodeledXML, unmodeled.Ycol); + six::getFirstAndOnly(parser(), unmodeledXML, unmodeled.XrowYcol); + + if (auto pUnmodeledDecorrXML = getOptionalUnmodeledDecorr(unmodeledXML, unmodeled.unmodeledDecorr)) { - auto& UnmodeledDecorr = *get(errorStatistics.Unmodeled->unmodeledDecorr); - - auto xrowXML = getFirstAndOnly(unmodeledDecorrXML, "Xrow"); - parseDouble_getFirstAndOnly(xrowXML, CorrCoefZero, UnmodeledDecorr.Xrow); - parseDouble_getFirstAndOnly(xrowXML, DecorrRate, UnmodeledDecorr.Xrow); - - auto ycolXML = getFirstAndOnly(unmodeledDecorrXML, "Ycol"); - parseDouble_getFirstAndOnly(ycolXML, CorrCoefZero, UnmodeledDecorr.Ycol); - parseDouble_getFirstAndOnly(ycolXML, DecorrRate, UnmodeledDecorr.Ycol); + const auto getFirstAndOnly_Xrow_Ycol = [&](XsElement& rc) + { + if (auto pXML = getFirstAndOnly(pUnmodeledDecorrXML, rc.tag())) + { + six::getFirstAndOnly(parser(), *pXML, value(rc).corrCoefZero); + six::getFirstAndOnly(parser(), *pXML, value(rc).decorrRate); + } + }; + + auto& unmodeledDecorr = value(unmodeled.unmodeledDecorr); + getFirstAndOnly_Xrow_Ycol(unmodeledDecorr.Xrow); + getFirstAndOnly_Xrow_Ycol(unmodeledDecorr.Ycol); } } diff --git a/six/modules/c++/six/source/Utilities.cpp b/six/modules/c++/six/source/Utilities.cpp index 0652332ba..3414e6201 100644 --- a/six/modules/c++/six/source/Utilities.cpp +++ b/six/modules/c++/six/source/Utilities.cpp @@ -837,12 +837,14 @@ void six::getErrors(const ErrorStatistics* errorStats, unmodeledErrorCovar(0, 1) = unmodeledErrorCovar(1, 0) = corr * (composite.rg * composite.az); } } - else if (const auto unmodeled = errorStats->Unmodeled.get()) + else if (has_value(errorStats->unmodeled)) { + const auto& unmodeled = value(errorStats->unmodeled); + auto& unmodeledErrorCovar = errors.mUnmodeledErrorCovar; - auto&& Xrow = unmodeled->Xrow; - auto&& Ycol = unmodeled->Ycol; - auto&& XrowYcol = unmodeled->XrowYcol; + auto&& Xrow = unmodeled.Xrow; + auto&& Ycol = unmodeled.Ycol; + auto&& XrowYcol = unmodeled.XrowYcol; // From Bill: Here is the mapping from the UnmodeledError to the 2x2 covariance matrix: // [0][0] = Xrow; [1][1] = Ycol; diff --git a/six/projects/csm/source/SIXSensorModel.cpp b/six/projects/csm/source/SIXSensorModel.cpp index 7ca6ed1c4..fef0c0547 100644 --- a/six/projects/csm/source/SIXSensorModel.cpp +++ b/six/projects/csm/source/SIXSensorModel.cpp @@ -1176,11 +1176,13 @@ std::vector SIXSensorModel::getSIXUnmodeledError_(const six::ErrorStatis { if (pErrorStatistics != nullptr) { - if (auto pUnmodeled = pErrorStatistics->Unmodeled.get()) + if (has_value(pErrorStatistics->unmodeled)) { - auto&& Xrow = pUnmodeled->Xrow; - auto&& Ycol = pUnmodeled->Ycol; - auto&& XrowYcol = pUnmodeled->XrowYcol; + const auto& unmodeled = value(pErrorStatistics->unmodeled); + + auto&& Xrow = unmodeled.Xrow; + auto&& Ycol = unmodeled.Ycol; + auto&& XrowYcol = unmodeled.XrowYcol; // From Bill: Here is the mapping from the UnmodeledError to the 2x2 covariance matrix: // [0][0] = Xrow; [1][1] = Ycol;