diff --git a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt index 65a22716b089dcebad8fd64d52a6fe5d83534bc1..c81bd112bc874c9e425ff97cbe6b99927c586bf1 100644 --- a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt +++ b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt @@ -56,3 +56,28 @@ LUT1D.ARRAY = 1 2 3" "NoData": "-10000", },] } +md5: {{"Extra.Comment": "Test Extrakeys", +"ProductType": "Official", +"ProjectionEPSG": "4326", +"ProjectionWKT": "UTM projRef", +"ProjectionProj": "+proj=longlat +datum=WGS84 +no_defs ", +"SpectralSensitivity": "LUT1D.DIM0.SIZE = 3 +LUT1D.DIM0.ORIGIN = 0 +LUT1D.DIM0.SPACING = 1 +LUT1D.ARRAY = 1 2 3" +"RPC": "<RPCParam>", +"ProductionDate": "2009-08-10T10:30:08.142149Z", +"GCP": "<GCPParam>", +"SensorID": "PHR", +}Bands[{"PhysicalBias": "1", +"BandName": "B3", +"PhysicalGain": "2", +},{"PhysicalBias": "2", +"BandName": "B2", +"PhysicalGain": "3", +},{"PhysicalBias": "3", +"PhysicalGain": "4", +"BandName": "B4", +"NoData": "-10000", +},] +} diff --git a/Modules/Core/Metadata/include/otbImageMetadata.h b/Modules/Core/Metadata/include/otbImageMetadata.h index 87ba36bc00d264fcb1977c86c5fee4017e07aeab..abd36d59eb523c6cf858bc7bf68160de506bb12c 100644 --- a/Modules/Core/Metadata/include/otbImageMetadata.h +++ b/Modules/Core/Metadata/include/otbImageMetadata.h @@ -24,6 +24,7 @@ #include "otbGeometryMetadata.h" #include "otbMetaDataKey.h" #include "OTBMetadataExport.h" +#include "otbMacro.h" #include <boost/any.hpp> #include <vector> @@ -237,6 +238,9 @@ public: class OTBMetadata_EXPORT ImageMetadata: public ImageMetadataBase { public: + /** Metadata object as a vector of Keywordlist */ + using KeywordlistVector = std::vector<ImageMetadata::Keywordlist>; + /** Band-specific metadatas */ using ImageMetadataBandsType = std::vector<ImageMetadataBase>; ImageMetadataBandsType Bands; @@ -262,6 +266,25 @@ public: /** if all bands share the same value of a key, put it at top level */ void compact(); + + /** Export the Metadata as a list of KeywordList + * The first KeywordList contains the metadata common to all the bands. + * The following KeywordList contain the metadata of the bands. + * */ + void ToKeywordlists(KeywordlistVector&) const; + + /** Export the bands of the Metadata as a list of KeywordList + * Each KeywordList contain the metadata of a band. + * */ + void ToBandKeywordlists(KeywordlistVector&) const; + + /** Import metadata from a list of keywordlist (will skip + * MDGeom::SensorGeometry). + * The first KeywordList contains the metadata common to all the bands. + * The following KeywordList contain the metadata of the bands. + * Returns True if all keywords were + * parsed correctly */ + bool FromKeywordlists(const KeywordlistVector&); }; extern OTBMetadata_EXPORT std::ostream& operator<<(std::ostream& os, const otb::ImageMetadataBase& imd); diff --git a/Modules/Core/Metadata/include/otbMetaDataKey.h b/Modules/Core/Metadata/include/otbMetaDataKey.h index d725e6b3fb21e56952f26feb72a5c84efefec93f..7caa6f54ac1cfe75704bb481c8c16c35d634a3ea 100644 --- a/Modules/Core/Metadata/include/otbMetaDataKey.h +++ b/Modules/Core/Metadata/include/otbMetaDataKey.h @@ -269,7 +269,8 @@ inline boost::bimap<T, std::string> bimapGenerator(std::map<T, std::string> inMa return bm; } -extern OTBMetadata_EXPORT std::map<MDGeom, std::string> MDGeomNames; +typedef boost::bimap<MDGeom, std::string> MDGeomBmType; +extern OTBMetadata_EXPORT MDGeomBmType MDGeomNames; typedef boost::bimap<MDNum, std::string> MDNumBmType; extern OTBMetadata_EXPORT MDNumBmType MDNumNames; diff --git a/Modules/Core/Metadata/src/otbImageMetadata.cxx b/Modules/Core/Metadata/src/otbImageMetadata.cxx index e6118f38bf23f2960928b48ed912f34ce950957f..ed37bde1177570b193fb68a6109dd8468af6ac8b 100644 --- a/Modules/Core/Metadata/src/otbImageMetadata.cxx +++ b/Modules/Core/Metadata/src/otbImageMetadata.cxx @@ -237,9 +237,10 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const oss.str(""); if (kv.first == MDGeom::RPC) { -// Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second); -// cast_string = rpcStruct.ToJSON(); oss << std::string("<RPCParam>"); + // TODO: replace this std::string by a correct serialization of the RPCParam when implemented + // Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second); + // cast_string << rpcStruct; } else if (kv.first == MDGeom::ProjectionEPSG) { @@ -247,9 +248,10 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const } else if (kv.first == MDGeom::GCP) { -// Projection::GCPParam gcpStruct = boost::any_cast<Projection::GCPParam>(kv.second); -// cast_string = gcpStruct.ToJSON(); oss << std::string("<GCPParam>"); + // TODO: replace this std::string by a correct serialization of the GCPParam when implemented + // Projection::GCPParam gcpStruct = boost::any_cast<Projection::GCPParam>(kv.second); + // cast_string << gcpStruct; } // TODO : MDGeom::SensorGeometry (should be exported as "<typeinfo>" where typeinfo is boost::any::type().name() // TODO : MDGeom::SAR @@ -258,8 +260,7 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const { oss << boost::any_cast<std::string>(kv.second); } - kwl.emplace(MetaData::MDGeomNames[kv.first], oss.str()); - + kwl.emplace(MetaData::MDGeomNames.left.at(kv.first), oss.str()); } // Converting the StringKeys for (const auto& kv : StringKeys) @@ -328,8 +329,37 @@ bool ImageMetadataBase::FromKeywordlist(const Keywordlist& kwl) // search iterators for (const auto& kv : kwl) { - // TODO Converting the GeomKeys - // skip MDGeom::SensorGeometry (they will be decoded by future SensorModelFactory) + // Converting the GeomKeys + auto geomKey = MetaData::MDGeomNames.right.find(kv.first); + if (geomKey != MetaData::MDGeomNames.right.end()) + { + if(geomKey->second == MDGeom::RPC) + { + Projection::RPCParam rpcParam; + // TODO: Uncomment when deserialization is implemented + //kv.second >> rpcParam; + this->Add(geomKey->second, rpcParam); + } + else if (geomKey->second == MDGeom::ProjectionEPSG) + { + this->Add(geomKey->second, Utils::LexicalCast<int>(kv.second.c_str(), "Keywordlist.second.c_str()")); + } + else if (geomKey->second == MDGeom::GCP) + { + Projection::GCPParam gcpParam; + // TODO: Uncomment when deserialization is implemented + //kv.second >> gcpParam; + this->Add(geomKey->second, gcpParam); + } + // TODO : MDGeom::SAR + // TODO : MDGeom::Adjustment + // skip MDGeom::SensorGeometry (they will be decoded by future SensorModelFactory) + else + { + this->Add(geomKey->second, kv.second); + } + continue; + } // Converting the StringKeys auto strKey = MetaData::MDStrNames.right.find(kv.first); if (strKey != MetaData::MDStrNames.right.end()) @@ -373,11 +403,13 @@ bool ImageMetadataBase::FromKeywordlist(const Keywordlist& kwl) } // Converting the ExtraKeys std::string prefix("Extra."); - if (kv.first.compare(0, prefix.size(), prefix)) + if (kv.first.compare(0, prefix.size(), prefix) == 0) { this->Add(kv.first.substr(prefix.size()), kv.second); continue; } + otbLogMacro(Warning, << "The metadata named '" << kv.first << "' with value '" << kv.second << "' was not parsed.") + all_parsed = false; } return all_parsed; } @@ -432,6 +464,40 @@ void ImageMetadata::compact() // TODO } +void ImageMetadata::ToKeywordlists(KeywordlistVector& kwlVect) const +{ + Keywordlist kwl; + this->ToKeywordlist(kwl); + kwlVect.push_back(kwl); + this->ToBandKeywordlists(kwlVect); +} + +void ImageMetadata::ToBandKeywordlists(KeywordlistVector& kwlVect) const +{ + Keywordlist kwl; + for (const auto& band: this->Bands) + { + band.ToKeywordlist(kwl); + kwlVect.push_back(kwl); + } +} + +bool ImageMetadata::FromKeywordlists(const KeywordlistVector& kwlVect) +{ + bool all_parsed = true; + auto kwlIt = kwlVect.cbegin(); + all_parsed = this->FromKeywordlist(*kwlIt) && all_parsed; + ++kwlIt; + while (kwlIt != kwlVect.cend()) + { + ImageMetadataBase imb; + all_parsed = imb.FromKeywordlist(*kwlIt) && all_parsed; + this->Bands.push_back(imb); + ++kwlIt; + } + return all_parsed; +} + // printing std::ostream& operator<<(std::ostream& os, const otb::ImageMetadataBase& imd) { diff --git a/Modules/Core/Metadata/src/otbMetaDataKey.cxx b/Modules/Core/Metadata/src/otbMetaDataKey.cxx index 324c816f3c375ffe7aeb3f9d70bec86e30160996..972d3ad8fe22e98ac95c39204a76deda20b357e2 100644 --- a/Modules/Core/Metadata/src/otbMetaDataKey.cxx +++ b/Modules/Core/Metadata/src/otbMetaDataKey.cxx @@ -262,9 +262,9 @@ void LUT<VDim>::FromString(std::string str) for (std::string line : lines) { boost::split(parts, line, [](char c){return c == '=';}); - if (std::stoi(parts[0].substr(3, 1)) != VDim) + boost::trim(parts[1]); + if (Utils::LexicalCast<int>(parts[0].substr(3, 1), "VDim") != VDim) throw std::invalid_argument("Wrong LUT dimension"); - std::string element = parts[0].substr(6, 5); if(parts[0].substr(6, 5) == "ARRAY") // this->Array { @@ -276,14 +276,14 @@ void LUT<VDim>::FromString(std::string str) } else { - unsigned int dim = std::stoi(parts[0].substr(9, 1)); + unsigned int dim = Utils::LexicalCast<int>(parts[0].substr(9, 1), "parts[0].substr(9, 1)"); if (dim > VDim) throw std::invalid_argument("LUT dimension higher than expected"); - element = parts[0].substr(11); + std::string element = parts[0].substr(11); if (element == "SIZE ") // this->Axis[dim].Size { - Axis[dim].Size = std::stoi(parts[1]); + Axis[dim].Size = Utils::LexicalCast<int>(parts[1], "Axis[dim].Size"); } else if (element == "VALUES ") // this->Axis[dim].Values @@ -294,15 +294,15 @@ void LUT<VDim>::FromString(std::string str) std::transform(str_array.begin(), str_array.end(), back_inserter(Axis[dim].Values), [](std::string const& val) {return std::stod(val);}); } - else if (element == "ORIGIN") + else if (element == "ORIGIN ") // this->Axis[dim].Origin { - Axis[dim].Origin = std::stoi(parts[1]); + Axis[dim].Origin = Utils::LexicalCast<double>(parts[1], "Axis[dim].Origin"); } - else if (element == "SPACING") + else if (element == "SPACING ") // this->Axis[dim].Spacing { - Axis[dim].Spacing = std::stoi(parts[1]); + Axis[dim].Spacing = Utils::LexicalCast<double>(parts[1], "Axis[dim].Spacing"); } } } @@ -370,7 +370,7 @@ MDL1DBmType MDL1DNames = bimapGenerator<MDL1D>(std::map<MDL1D, std::string> { MDL2DBmType MDL2DNames = bimapGenerator<MDL2D>(std::map<MDL2D, std::string> {}); -std::map<MDGeom, std::string> MDGeomNames = { +MDGeomBmType MDGeomNames = bimapGenerator<MDGeom>(std::map<MDGeom, std::string> { {MDGeom::ProjectionWKT, "ProjectionWKT"}, {MDGeom::ProjectionEPSG, "ProjectionEPSG"}, {MDGeom::ProjectionProj, "ProjectionProj"}, @@ -379,7 +379,7 @@ std::map<MDGeom, std::string> MDGeomNames = { {MDGeom::SensorGeometry, "SensorGeometry"}, {MDGeom::GCP, "GCP"}, {MDGeom::Adjustment, "Adjustment"} -}; +}); } // end namespace MetaData diff --git a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx index 8aa8cbb147110014025a1f1ad2139c760ece22be..09a1c0981fca80b46655dbabe8fb4270d89aee89 100644 --- a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx +++ b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx @@ -200,8 +200,8 @@ int otbImageMetadataTest(int argc, char* argv[]) md4.Add(MDGeom::GCP, gcpStruct); MetaData::LUT1D lut1d; lut1d.Axis[0].Size = 3; - lut1d.Axis[0].Origin = 0; - lut1d.Axis[0].Spacing = 1; + lut1d.Axis[0].Origin = 0.; + lut1d.Axis[0].Spacing = 1.; std::vector<double>array({1.0, 2.0, 3.0}); lut1d.Array = {1.0, 2.0, 3.0}; md4.Add(MDL1D::SpectralSensitivity, lut1d); @@ -210,6 +210,12 @@ int otbImageMetadataTest(int argc, char* argv[]) md3.append(md4); outfile << "md3_append: "<< md3 << "\n"; + ImageMetadata::KeywordlistVector kwlVect; + md3.ToKeywordlists(kwlVect); + ImageMetadata md5; + md5.FromKeywordlists(kwlVect); + outfile << "md5: "<< md5 << "\n"; + outfile.close(); return EXIT_SUCCESS; }