From f312b81c8ac429e21b0fdde8254f5583d6faaf33 Mon Sep 17 00:00:00 2001
From: Julien Osman <julien.osman@c-s.fr>
Date: Mon, 20 Apr 2020 17:31:30 +0200
Subject: [PATCH] REFAC: LUT1D and LUT2D can now serialize. ToKeywordlist
 updated to use serialized LUT. Fixed typo Axes to Axis

---
 .../OTB/Files/ioTvcoreImageMetadataTest.txt   |  9 +-
 .../Core/Metadata/include/otbMetaDataKey.h    |  9 +-
 .../Core/Metadata/src/otbImageMetadata.cxx    | 14 +--
 Modules/Core/Metadata/src/otbMetaDataKey.cxx  | 87 ++++++++++++++++++-
 .../Metadata/test/otbImageMetadataTest.cxx    |  6 +-
 5 files changed, 109 insertions(+), 16 deletions(-)

diff --git a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
index e049285504..ce22958d22 100644
--- a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
+++ b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
@@ -36,10 +36,13 @@ md3_append: {{"Extra.Comment": "Test Extrakeys",
 "ProjectionEPSG": "4326",
 "ProjectionWKT": "UTM projRef",
 "ProjectionProj": "+proj=longlat +datum=WGS84 +no_defs ",
-"SpectralSensitivity": "{"Axes": [{"Size": "3", "Origin": "0", "Spacing": "1", "Values": []}, ], "Array": [1, 2, 3, ]}",
-"RPC": "{"LineOffset": "0", "SampleOffset": "0", "LatOffset": "0", "LonOffset": "0", "HeightOffset": "0", "LineScale": "0", "SampleScale": "0", "LatScale": "0", "LonScale": "0", "HeightScale": "0", "LineNum": [ "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0", ], "LineDen": [ "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0", ], "SampleNum": [ "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0", ], "SampleDen": [ "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0",  "0", ], }",
+"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": "{"Projection": "", [{"GCP_Id": "", "GCP_Info": "", "GCP_Row": "0", "GCP_Col": "0", "GCP_X": "0", "GCP_Y": "0", "GCP_Z": "0", }, ]}",
+"GCP": "<GCPParam>",
 "SensorID": "PHR",
 }Bands[{"PhysicalBias": "1",
 "BandName": "B3",
diff --git a/Modules/Core/Metadata/include/otbMetaDataKey.h b/Modules/Core/Metadata/include/otbMetaDataKey.h
index 4c612da182..8bb71f79e7 100644
--- a/Modules/Core/Metadata/include/otbMetaDataKey.h
+++ b/Modules/Core/Metadata/include/otbMetaDataKey.h
@@ -26,6 +26,7 @@
 #include <cstdio>
 
 #include <boost/bimap.hpp>
+#include <boost/algorithm/string.hpp>
 
 #include "itkDataObject.h"
 #include "itkVariableLengthVector.h"
@@ -242,11 +243,15 @@ struct LUTAxis
 template <unsigned int VDim> class LUT
 {
 public:
-  LUTAxis Axes[VDim];
+  LUTAxis Axis[VDim];
   
   std::vector<double> Array;
 
-  std::string ToJSON(bool multiline=false) const;
+  std::string OTBMetadata_EXPORT ToJSON(bool multiline=false) const;
+
+  std::string OTBMetadata_EXPORT ToString() const;
+
+  void OTBMetadata_EXPORT FromString(std::string);
 };
 
 typedef LUT<1> LUT1D;
diff --git a/Modules/Core/Metadata/src/otbImageMetadata.cxx b/Modules/Core/Metadata/src/otbImageMetadata.cxx
index d61ba6196b..927ecd99aa 100644
--- a/Modules/Core/Metadata/src/otbImageMetadata.cxx
+++ b/Modules/Core/Metadata/src/otbImageMetadata.cxx
@@ -218,8 +218,9 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const
 
     if (kv.first == MDGeom::RPC)
     {
-      Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second);
-      cast_string = rpcStruct.ToJSON();
+//      Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second);
+//      cast_string = rpcStruct.ToJSON();
+      cast_string = std::string("<RPCParam>");
     }
     else if (kv.first == MDGeom::ProjectionEPSG)
     {
@@ -227,8 +228,9 @@ 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();
+//      Projection::GCPParam gcpStruct = boost::any_cast<Projection::GCPParam>(kv.second);
+//      cast_string = gcpStruct.ToJSON();
+      cast_string = std::string("<GCPParam>");
     }
     // TODO : MDGeom::SensorGeometry (should be exported as "<typeinfo>" where typeinfo is boost::any::type().name()
     // TODO : MDGeom::SAR
@@ -255,12 +257,12 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const
   // Converting the LUT1DKeys
   for (const auto& kv : LUT1DKeys)
   {
-    kwl.emplace(MetaData::MDL1DNames[kv.first], kv.second.ToJSON());
+    kwl.emplace(MetaData::MDL1DNames[kv.first], kv.second.ToString());
   }
   // Convereting the LUT2DKeys
   for (const auto& kv : LUT2DKeys)
   {
-    kwl.emplace(MetaData::MDL2DNames[kv.first], kv.second.ToJSON());
+    kwl.emplace(MetaData::MDL2DNames[kv.first], kv.second.ToString());
   }
   // Converting the TimeKeys
   for (const auto& kv : TimeKeys)
diff --git a/Modules/Core/Metadata/src/otbMetaDataKey.cxx b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
index 4db371acfe..bc34eb734b 100644
--- a/Modules/Core/Metadata/src/otbMetaDataKey.cxx
+++ b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
@@ -216,9 +216,9 @@ std::string LUT<VDim>::ToJSON(bool multiline) const
     sep = "\n";
   }
   oss << "{"
-      << "\"Axes\": [";
+      << "\"Axis\": [";
   for (unsigned int loop = 0 ; loop < VDim  ; loop++)
-    oss << Axes[loop].ToJSON(multiline) << ", ";
+    oss << Axis[loop].ToJSON(multiline) << ", ";
   oss << "], " << sep
       << "\"Array\": [";
   for (const auto& value : Array)
@@ -227,6 +227,89 @@ std::string LUT<VDim>::ToJSON(bool multiline) const
   return oss.str();
 }
 
+template <unsigned int VDim>
+std::string LUT<VDim>::ToString() const
+{
+  std::ostringstream oss;
+  for (unsigned int dim = 0 ; dim < VDim ; dim++)
+  {
+    oss << "LUT" << VDim << "D.DIM" << dim << ".SIZE = " << Axis[dim].Size << "\n";
+    if (Axis[dim].Values.size() > 0)
+    // Irregular sampling
+    {
+      oss << "LUT" << VDim << "D.DIM" << dim << ".VALUES = ";
+      for (const auto& value : Axis[dim].Values)
+        oss << value << " ";
+      oss << "\n";
+    }
+    else
+    {
+    // Regular sampling
+      oss << "LUT" << VDim << "D.DIM" << dim << ".ORIGIN = " << Axis[dim].Origin << "\n"
+          << "LUT" << VDim << "D.DIM" << dim << ".SPACING = " << Axis[dim].Spacing << "\n";
+    }
+  }
+  oss << "LUT" << VDim << "D.ARRAY = ";
+  for (const auto& value : Array)
+    oss << value << " ";
+  return oss.str();
+}
+
+template <unsigned int VDim>
+void LUT<VDim>::FromString(std::string str)
+{
+  std::vector<std::string> lines;
+  std::vector<std::string> parts;
+  boost::split(lines, str, [](char c){return c == '\n';});
+  for (std::string line : lines)
+  {
+    boost::split(parts, line, [](char c){return c == '=';});
+    if (std::stoi(parts[0].substr(3, 1)) != 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
+    {
+      std::vector<std::string> str_array;
+      boost::split(str_array, parts[1], [](char c){return c == ' ';});
+      Array.reserve(str_array.size());
+      std::transform(str_array.begin(), str_array.end(), back_inserter(Array),
+                      [](std::string const& val) {return std::stod(val);});
+    }
+    else
+    {
+      unsigned int dim = std::stoi(parts[0].substr(9, 1));
+      if (dim > VDim)
+        throw std::invalid_argument("LUT dimension higher than expected");
+      element = parts[0].substr(11);
+      if (element == "SIZE ")
+      // this->Axis[dim].Size
+      {
+        Axis[dim].Size = std::stoi(parts[1]);
+      }
+      else if (element == "VALUES ")
+      // this->Axis[dim].Values
+      {
+        std::vector<std::string> str_array;
+        boost::split(str_array, parts[1], [](char c){return c == ' ';});
+        Axis[dim].Values.reserve(str_array.size());
+        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")
+      // this->Axis[dim].Origin
+      {
+        Axis[dim].Origin = std::stoi(parts[1]);
+      }
+      else if (element == "SPACING")
+      // this->Axis[dim].Spacing
+      {
+        Axis[dim].Spacing = std::stoi(parts[1]);
+      }
+    }
+  }
+}
+
 template class LUT<1>;
 template class LUT<2>;
 
diff --git a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
index dc00543683..8aa8cbb147 100644
--- a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
+++ b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
@@ -199,9 +199,9 @@ int otbImageMetadataTest(int argc, char* argv[])
   gcpStruct.GCPs.push_back(OTB_GCP());
   md4.Add(MDGeom::GCP, gcpStruct);
   MetaData::LUT1D lut1d;
-  lut1d.Axes[0].Size = 3;
-  lut1d.Axes[0].Origin = 0;
-  lut1d.Axes[0].Spacing = 1;
+  lut1d.Axis[0].Size = 3;
+  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);
-- 
GitLab