diff --git a/Modules/Core/Metadata/include/otbGeometryMetadata.h b/Modules/Core/Metadata/include/otbGeometryMetadata.h index b2be4192455ba1b15fbac1401d81a0879dee26ea..acde2439bde178776953fdd4e8bc5b9ed1644f9f 100644 --- a/Modules/Core/Metadata/include/otbGeometryMetadata.h +++ b/Modules/Core/Metadata/include/otbGeometryMetadata.h @@ -69,8 +69,15 @@ public: void Print(std::ostream& os) const; std::string ToJSON(bool multiline=false) const; + + /** Keywordlist export */ + void ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const; + + /** Keywordlist import */ + static GCP FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix); }; + namespace Projection { @@ -86,6 +93,12 @@ struct OTBMetadata_EXPORT GCPParam // JSON export std::string ToJSON(bool multiline=false) const; + + /** Keywordlist export */ + void ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const; + + /** Keywordlist import */ + void FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix); }; /** \struct RPCParam diff --git a/Modules/Core/Metadata/src/otbGeometryMetadata.cxx b/Modules/Core/Metadata/src/otbGeometryMetadata.cxx index 0dadc355c40c57bf0575a36db318f76348ca54b9..8fcc827202c50f1d6f70ece349cc14c02818d53a 100644 --- a/Modules/Core/Metadata/src/otbGeometryMetadata.cxx +++ b/Modules/Core/Metadata/src/otbGeometryMetadata.cxx @@ -22,6 +22,53 @@ #include +namespace +{ +constexpr int STRING_PRECISION = 20; + +// the precision of std::to_string is limited to 6 digits +template +std::string to_string_with_precision(const T value) +{ + std::ostringstream out; + out.precision(STRING_PRECISION); + out << std::fixed << value; + return out.str(); +} + +template +void KeywordlistToVector(std::vector & vector, + const otb::MetaData::Keywordlist & kwl, + const std::string & prefix) +{ + vector.clear(); + + const auto size = std::stoi(kwl.at(prefix + ".number")); + for (int i = 0; i < size; i++) + { + auto t = T::FromKeywordlist(kwl, prefix + "_" + to_string_with_precision(i) + "."); + vector.push_back(t); + } +} + + +template +void VectorToKeywordList(otb::MetaData::Keywordlist & kwl, + const std::vector & input, + const std::string & prefix) +{ + int i = 0; + for (const auto & elem: input) + { + elem.ToKeywordlist(kwl, prefix + "_" + to_string_with_precision(i) + "."); + i++; + } + kwl.insert({prefix + ".number" , to_string_with_precision(i)}); +} + +} + + namespace otb { @@ -58,6 +105,36 @@ std::string GCP::ToJSON(bool multiline) const return oss.str(); } +void GCP::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const +{ + kwl.insert({prefix + "Id", m_Id}); + kwl.insert({prefix + "Info", m_Info}); + kwl.insert({prefix + "Row", to_string_with_precision(m_GCPRow)}); + kwl.insert({prefix + "Col", to_string_with_precision(m_GCPCol)}); + kwl.insert({prefix + "X", to_string_with_precision(m_GCPX)}); + kwl.insert({prefix + "Y", to_string_with_precision(m_GCPY)}); + kwl.insert({prefix + "Z", to_string_with_precision(m_GCPZ)}); +} + +GCP GCP::FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix) +{ + //Info is optional in GCPs, the key might not be in the keywordlist + std::string info; + auto infoFound = kwl.find(prefix + "Info"); + if (infoFound != kwl.end()) + { + info = infoFound->second; + } + + return GCP(kwl.at(prefix + "Id"), + info, + std::stod(kwl.at(prefix + "Row")), + std::stod(kwl.at(prefix + "Col")), + std::stod(kwl.at(prefix + "X")), + std::stod(kwl.at(prefix + "Y")), + std::stod(kwl.at(prefix + "Z"))); +} + namespace Projection { std::string GCPParam::ToJSON(bool multiline) const @@ -77,6 +154,18 @@ std::string GCPParam::ToJSON(bool multiline) const return oss.str(); } +void GCPParam::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & prefix) const +{ + kwl.insert({prefix + "GCPProjection", GCPProjection}); + VectorToKeywordList(kwl, GCPs, prefix + "GCP"); +} + +void GCPParam::FromKeywordlist(const MetaData::Keywordlist & kwl, const std::string & prefix) +{ + GCPProjection = kwl.at(prefix + "GCPProjection"); + KeywordlistToVector(GCPs, kwl, prefix + "GCP"); +} + std::string RPCParam::ToJSON(bool multiline) const { std::ostringstream oss; diff --git a/Modules/Core/Metadata/src/otbSARMetadata.cxx b/Modules/Core/Metadata/src/otbSARMetadata.cxx index 74a381b31b97c5dee00e101a5dd0f19dc76122f7..f257ed33281c6d9cd30561b958a0e9ac78d383df 100644 --- a/Modules/Core/Metadata/src/otbSARMetadata.cxx +++ b/Modules/Core/Metadata/src/otbSARMetadata.cxx @@ -392,7 +392,6 @@ void SARCalib::ToKeywordlist(MetaData::Keywordlist & kwl, const std::string & pr // MetaData::Time std::ostringstream oss; oss << calibrationStartTime; - std::cout << "***** " << oss.str() << std::endl; kwl.insert({prefix + "CalibrationStartTime", oss.str()}); oss.str(""); oss << calibrationStopTime; diff --git a/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx b/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx index cfe31f1a6c0839756ace7c4a4090cca4fee9f254..006c9f81d86ec1ce14b7c88aee4ce691ef5e6ba6 100644 --- a/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx +++ b/Modules/Core/Metadata/src/otbSentinel1ImageMetadataInterface.cxx @@ -265,14 +265,16 @@ NoiseVectorLists ReadNoiseVectorListsFromXML(const MetadataSupplierInterface& md } std::string azimuthNoisePrefix = "noise.noiseAzimuthVectorList."; - std::string azimuthVectorName = "noiseAzimuthVector_"; + std::string azimuthVectorName = "noiseAzimuthVector"; std::string azimuthLUTName = "noiseAzimuthLut"; const int azimuthCount = mds.GetAs(0, azimuthNoisePrefix + "count"); for (int i = 0; i < azimuthCount; i++) { - const auto prefix = azimuthNoisePrefix + azimuthVectorName + std::to_string(i+1) + "."; + const auto prefix = azimuthCount == 1 + ? azimuthNoisePrefix + azimuthVectorName + "." + : azimuthNoisePrefix + azimuthVectorName + "_" + std::to_string(i+1) + "."; Sentinel1AzimuthNoiseStruct azimuthNoiseVector; azimuthNoiseVector.firstAzimuthLine = mds.GetAs(prefix + "firstAzimuthLine"); diff --git a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx index 3eb2cf14dae1a50013bc03c2d9a31177d081bab4..18ff394e39100ee28fb46b1de785b22e9bdb8dce 100644 --- a/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx +++ b/Modules/IO/IOGDAL/src/otbGDALImageIO.cxx @@ -41,7 +41,6 @@ #include "ogr_spatialref.h" #include "ogr_srs_api.h" - #include "itksys/RegularExpression.hxx" #include "otbGDALDriverManagerWrapper.h" @@ -1314,15 +1313,6 @@ void GDALImageIO::InternalWriteImageInformation(const void* buffer) m_Imd.Bands = bandRangeMetadata; } - // TODO : this should be a warning instead of an exception - // For complex pixels the number of bands is twice the number of components (in GDAL sense) - if ( !m_Imd.Bands.empty() - && static_cast(m_NbBands) != m_Imd.Bands.size() - && !((m_Imd.Bands.size() == static_cast(2 * m_NbBands)) && this->GetPixelType() == COMPLEX)) - { - itkExceptionMacro(<< "Number of bands in metadata inconsistent with actual image."); - } - if ((m_Dimensions[0] == 0) && (m_Dimensions[1] == 0)) { itkExceptionMacro(<< "Dimensions are not defined."); @@ -1550,6 +1540,18 @@ void GDALImageIO::InternalWriteImageInformation(const void* buffer) MetaData::Keywordlist SARKwl; const auto & param = boost::any_cast(m_Imd[MDGeom::SAR]); param.ToKeywordlist(SARKwl, "SAR."); + + + // Write GCP as metadata (not GDAL gcps) because SAR sensor images needs GCPs and + // might also have a geotransform (e.g. after a ROI extraction), which is undefined + // in GDAL. Note that the geotransform is not applied to the GCPs in this case + if (m_Imd.Has(MDGeom::GCP)) + { + const Projection::GCPParam & gcpParam = + boost::any_cast(m_Imd[MDGeom::GCP]); + gcpParam.ToKeywordlist(SARKwl, "SAR."); + } + for (auto & key: SARKwl) { dataset->SetMetadataItem(key.first.c_str(), key.second.c_str()); @@ -1907,6 +1909,12 @@ void GDALImageIO::ExportMetadata() // be prefixed by: MDGeomNames[MDGeom::SensorGeometry] + '.' ImageMetadataBase::Keywordlist kwl; m_Imd.ToKeywordlist(kwl); + if (m_Imd.Has(MDGeom::SARCalib)) + { + const auto & param = boost::any_cast(m_Imd[MDGeom::SARCalib]); + param.ToKeywordlist(kwl, "SARCalib."); + } + KeywordlistToMetadata(kwl); int bIdx = 0; @@ -1939,6 +1947,10 @@ void GDALImageIO::ImportMetadata() otb::SARParam sar; sar.FromKeywordlist(kwl, "SAR."); m_Imd.Add(MDGeom::SAR, sar); + + otb::Projection::GCPParam gcps; + gcps.FromKeywordlist(kwl, "SAR."); + m_Imd.Add(MDGeom::GCP, gcps); } catch(const std::exception& e) { @@ -1946,6 +1958,20 @@ void GDALImageIO::ImportMetadata() } } + // Decode SAR metadata + if (kwl.find("SARCalib") != kwl.end()) + { + try + { + otb::SARCalib sarCalib; + sarCalib.FromKeywordlist(kwl, "SARCalib."); + m_Imd.Add(MDGeom::SARCalib, sarCalib); + } + catch(const std::exception& e) + { + otbLogMacro(Warning, << "Input image has SAR calibration metadata, but OTB was not able to read it: " << e.what()); + } + } m_Imd.FromKeywordlist(kwl); // Parsing the bands @@ -1955,7 +1981,6 @@ void GDALImageIO::ImportMetadata() GDALMetadataToKeywordlist(m_Dataset->GetDataSet()->GetRasterBand(band+1)->GetMetadata(), kwl); m_Imd.Bands[band].FromKeywordlist(kwl); } - } void GDALImageIO::KeywordlistToMetadata(ImageMetadataBase::Keywordlist kwl, int band)