From aeb69fd5a305c450d4517517b1fee0bf844145d6 Mon Sep 17 00:00:00 2001
From: Ludovic Hussonnois <ludovic.hussonnois@c-s.fr>
Date: Thu, 23 Feb 2017 16:51:47 +0100
Subject: [PATCH] ENH: Update SampleSelection application to take only image.

When only image is provided to the application a default vector
corresponding to the image envelope is generated.
---
 .../app/otbSampleSelection.cxx                | 119 +++++++++++++++---
 1 file changed, 102 insertions(+), 17 deletions(-)

diff --git a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx
index 81b2b33f02..96cd34c8ee 100644
--- a/Modules/Applications/AppClassification/app/otbSampleSelection.cxx
+++ b/Modules/Applications/AppClassification/app/otbSampleSelection.cxx
@@ -15,6 +15,8 @@
  PURPOSE.  See the above copyright notices for more information.
 
  =========================================================================*/
+#include "otbImageToEnvelopeVectorDataFilter.h"
+#include "otbVectorDataFileWriter.h"
 #include "otbWrapperApplication.h"
 #include "otbWrapperApplicationFactory.h"
 #include "otbSamplingRateCalculator.h"
@@ -77,6 +79,11 @@ private:
     m_RateCalculator = RateCalculatorType::New();
     }
 
+  ~SampleSelection()
+  {
+    RemoveFile( m_DefaultVectorDataFileName );
+  }
+
   void DoInit()
   {
     SetName("SampleSelection");
@@ -85,8 +92,8 @@ private:
     // Documentation
     SetDocName("Sample Selection");
     SetDocLongDescription("The application selects a set of samples from geometries "
-      "intended for training (they should have a field giving the associated "
-      "class). \n\nFirst of all, the geometries must be analyzed by the PolygonClassStatistics application "
+      "intended for training. If no geometries are provided only one class is used for the entire image. "
+      "\n\nFirst of all, provided geometries must be analyzed by the PolygonClassStatistics application "
       "to compute statistics about the geometries, which are summarized in an xml file. "
       "\nThen, this xml file must be given as input to this application (parameter instats).\n\n"
       "The input support image and the input training vectors shall be given in "
@@ -133,12 +140,14 @@ private:
 
     AddParameter(ParameterType_InputFilename, "vec", "Input vectors");
     SetParameterDescription("vec","Input geometries to analyse");
+    MandatoryOff("vec");
 
     AddParameter(ParameterType_OutputFilename, "out", "Output vectors");
     SetParameterDescription("out","Output resampled geometries");
 
     AddParameter(ParameterType_InputFilename, "instats", "Input Statistics");
     SetParameterDescription("instats","Input file storing statistics (XML format)");
+    MandatoryOff("instats");
 
     AddParameter(ParameterType_OutputFilename, "outrates", "Output rates");
     SetParameterDescription("outrates","Output rates (CSV formatted)");
@@ -202,6 +211,7 @@ private:
     AddParameter(ParameterType_ListView, "field", "Field Name");
     SetParameterDescription("field","Name of the field carrying the class name in the input vectors.");
     SetListViewSingleSelectionMode("field",true);
+    MandatoryOff("field");
 
     AddParameter(ParameterType_Int, "layer", "Layer Index");
     SetParameterDescription("layer", "Layer index to read in the input vector file.");
@@ -226,6 +236,8 @@ private:
   {
  if ( HasValue("vec") )
       {
+      MandatoryOn("instats");
+      MandatoryOn("field");
       std::string vectorFile = GetParameterString("vec");
       ogr::DataSource::Pointer ogrDS =
         ogr::DataSource::New(vectorFile, ogr::DataSource::Modes::Read);
@@ -250,8 +262,15 @@ private:
           }
         }
       }
+ else
+   {
+   MandatoryOff( "vec" );
+   MandatoryOff( "field" );
+   MandatoryOff( "instats" );
+   }
   }
 
+
   void DoExecute()
     {
     // Clear state
@@ -259,19 +278,35 @@ private:
 
     otb::Wrapper::ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(this,"elev");
 
-    // Get field name
-    std::vector<int> selectedCFieldIdx = GetSelectedItems("field");
-    
-    if(selectedCFieldIdx.empty())
-      {
-      otbAppLogFATAL(<<"No field has been selected for data labelling!");
-      }
-    
-    std::vector<std::string> cFieldNames = GetChoiceNames("field");  
-    std::string fieldName = cFieldNames[selectedCFieldIdx.front()];
-    
-    m_ReaderStat->SetFileName(this->GetParameterString("instats"));
-    ClassCountMapType classCount = m_ReaderStat->GetStatisticMapByName<ClassCountMapType>("samplesPerClass");
+      ClassCountMapType classCount;
+      std::string fieldName;
+
+      if(HasValue("vec"))
+        {
+        // Get field name
+        std::vector<int> selectedCFieldIdx = GetSelectedItems("field");
+
+        if(selectedCFieldIdx.empty())
+          {
+          otbAppLogFATAL(<<"No field has been selected for data labelling!");
+          }
+
+        std::vector<std::string> cFieldNames = GetChoiceNames("field");
+        fieldName = cFieldNames[selectedCFieldIdx.front()];
+
+        m_ReaderStat->SetFileName(this->GetParameterString("instats"));
+        classCount = m_ReaderStat->GetStatisticMapByName<ClassCountMapType>("samplesPerClass");
+
+        }
+      else
+        {
+        // In case of image only sample selection use all pixels and first
+        m_DefaultVectorDataFileName = GetParameterString( "out" ) + "TmpVectorData.shp";
+        FloatVectorImageType::Pointer image = GetParameterImage("in");
+        fieldName = GenerateVectorDataFile(image, m_DefaultVectorDataFileName);
+        classCount["0"] = image->GetLargestPossibleRegion().GetNumberOfPixels();
+        }
+
     m_RateCalculator->SetClassCount(classCount);
     
     switch (this->GetParameterInt("strategy"))
@@ -355,8 +390,8 @@ private:
       }
 
     // Open input geometries
-    otb::ogr::DataSource::Pointer vectors =
-      otb::ogr::DataSource::New(this->GetParameterString("vec"));
+    std::string vectorDataFileName = HasValue( "vec" ) ? this->GetParameterString( "vec" ) : m_DefaultVectorDataFileName;
+    otb::ogr::DataSource::Pointer vectors = otb::ogr::DataSource::New( vectorDataFileName );
 
     // Reproject geometries
     FloatVectorImageType::Pointer inputImg = this->GetParameterImage("in");
@@ -452,6 +487,56 @@ private:
 
   RateCalculatorType::Pointer m_RateCalculator;
   XMLReaderType::Pointer m_ReaderStat;
+  std::string m_DefaultVectorDataFileName;
+
+  /**
+   * Write on the disk an vector data corresponding to the input image envelope
+   * \param floatVectorImage
+   * \param name output file name
+   * \return the default class name of the layer
+   */
+  std::string GenerateVectorDataFile(const FloatVectorImageType::Pointer &floatVectorImage, std::string name)
+  {
+    typedef otb::ImageToEnvelopeVectorDataFilter<FloatVectorImageType, VectorDataType> ImageToEnvelopeFilterType;
+    typedef ImageToEnvelopeFilterType::OutputVectorDataType OutputVectorData;
+    typedef otb::VectorDataFileWriter<OutputVectorData> VectorDataWriter;
+
+    ImageToEnvelopeFilterType::Pointer imageToEnvelopeVectorData = ImageToEnvelopeFilterType::New();
+    imageToEnvelopeVectorData->SetInput( floatVectorImage );
+    imageToEnvelopeVectorData->SetOutputProjectionRef( floatVectorImage->GetProjectionRef().c_str() );
+    OutputVectorData::Pointer vectorData = imageToEnvelopeVectorData->GetOutput();
+
+    // write temporary generated vector file to disk.
+    VectorDataWriter::Pointer vectorDataFileWriter = VectorDataWriter::New();
+    vectorDataFileWriter->SetInput( vectorData );
+    vectorDataFileWriter->SetFileName( name.c_str() );
+    vectorDataFileWriter->Write();
+    return "FID";
+  }
+
+  bool RemoveFile(std::string &filePath)
+  {
+    bool res = true;
+    if( itksys::SystemTools::FileExists( filePath.c_str() ) )
+      {
+      size_t posExt = filePath.rfind( '.' );
+      if( posExt != std::string::npos && filePath.compare( posExt, std::string::npos, ".shp" ) == 0 )
+        {
+        std::string shxPath = filePath.substr( 0, posExt ) + std::string( ".shx" );
+        std::string dbfPath = filePath.substr( 0, posExt ) + std::string( ".dbf" );
+        std::string prjPath = filePath.substr( 0, posExt ) + std::string( ".prj" );
+        RemoveFile( shxPath );
+        RemoveFile( dbfPath );
+        RemoveFile( prjPath );
+        }
+      res = itksys::SystemTools::RemoveFile( filePath.c_str() );
+      if( !res )
+        {
+        //otbAppLogINFO( <<"Unable to remove file  "<<filePath );
+        }
+      }
+    return res;
+  }
 };
 
 } // end of namespace Wrapper
-- 
GitLab