diff --git a/Modules/IO/IOGDAL/include/otbDEMHandler.h b/Modules/IO/IOGDAL/include/otbDEMHandler.h
index c566b1a5f8d396162f5951dbe8a5d7504a6ad0f6..d334adb206047dabb99ba37bdb8c0e76c3cac68a 100644
--- a/Modules/IO/IOGDAL/include/otbDEMHandler.h
+++ b/Modules/IO/IOGDAL/include/otbDEMHandler.h
@@ -4,12 +4,35 @@
 #include "otbImage.h"
 #include "otbGDALDriverManagerWrapper.h"
 
-// C++ 17 : use std::optional instead
-#include <boost/optional.hpp>
-
 namespace otb
 {
-/**
+/** \class DEMHandler
+ *
+ * \brief Single access point for DEM data retrieval
+ *
+ * This class is the single configuration and access point for
+ * elevation handling in images projections and localization
+ * functions. Since this class is a singleton, there is no New() method. The
+ * DEMHandler::Instance() method should be used instead.
+ *
+ * Please be aware that a proper instantiation and parameter setting
+ * of this class is advised before any call to geometric filters or
+ * functionalities.
+ *
+ * The class allows configuring a directory containing DEM tiles
+ * (raster tiles will be opened by GDAL drivers) using the OpenDEMDirectory() 
+ * method. the OpenGeoidFile() method allows inputting a geoid file as well. 
+ * Last, a default height above ellipsoid can be set using the
+ * SetDefaultHeightAboveEllipsoid() method.
+ *
+ * The class allows retrieving either height above ellipsoid or
+ * height above Mean Sea Level (MSL).
+ *
+ * Here is the complete description of both methods output depending
+ * on the class configuration for the SRTM DEM (in the following, no
+ * SRTM means DEMDirectory not set, or no coverage for point, or
+ * srtm_value is no_data).
+ *
  * GetHeightAboveEllipsoid():
  * - SRTM and geoid both available: srtm_value + geoid_offset
  * - No SRTM but geoid available: geoid_offset
@@ -21,17 +44,20 @@ namespace otb
  * - No SRTM but geoid available: 0
  * - SRTM available, but no geoid: srtm_value
  * - No SRTM and no geoid available: 0
- * 
-*/
-
+ *
+ * \ingroup OTBIOGDAL
+ */
 class DEMHandler: public itk::Object
 {
 public:
-  /** Standard class typedefs. */
+  /** @name Standard class typedefs
+  * @{
+  */
   using Self =          DEMHandler;
   using Superclass =    itk::Object;
   using Pointer =       itk::SmartPointer<Self>;
   using ConstPointer =  itk::SmartPointer<const Self>;
+  /** @} */
 
   using PointType =     itk::Point<double, 2>;
 
@@ -41,51 +67,75 @@ public:
   /** Run-time type information (and related methods). */
   itkTypeMacro(DEMHandler, Object);
 
-  /** Try to open the DEM directory. */
+  /** Try to open the DEM directory. 
+  * \param path input path
+  */
   void OpenDEMFile(const std::string & path);
 
-  /** Try to open the DEM directory. */
-  void OpenDEMDirectory(const char* DEMDirectory);
+  /** Try to open the DEM directory. 
+  * \param DEMDirectory input directory
+  */
   void OpenDEMDirectory(const std::string& DEMDirectory);
 
-  /** return true if the directory contain raster */
-  bool IsValidDEMDirectory(const char* DEMDirectory);
-
-  bool OpenGeoidFile(const char* geoidFile);
+  /** return true if the directory contain raster 
+  * \param DEMDirectory input directory
+  */
+  bool IsValidDEMDirectory(const std::string& DEMDirectory);
 
+  /** Try to open a geoid file 
+  * \param geoidFile input geoid path
+  */
   bool OpenGeoidFile(const std::string& geoidFile);
 
+  /** Return the height above the ellipsoid :
+   * - SRTM and geoid both available: srtm_value + geoid_offset
+   * - No SRTM but geoid available: geoid_offset
+   * - SRTM available, but no geoid: srtm_value
+   * - No SRTM and no geoid available: default height above ellipsoid
+   * \param lon input longitude
+   * \param lat input latitude
+   * \return height above ellipsoid
+  */
   double GetHeightAboveEllipsoid(double lon, double lat);
 
   double GetHeightAboveEllipsoid(const PointType& geoPoint);
-
+ 
+  /** Return the height above the mean sea level :
+   * - SRTM and geoid both available: srtm_value
+   * - No SRTM but geoid available: 0
+   * - SRTM available, but no geoid: srtm_value
+   * - No SRTM and no geoid available: 0
+   * \param lon input longitude
+   * \param lat input latitude
+   * \return height above mean sea level
+  */
   double GetHeightAboveMSL(double lon, double lat);
 
   double GetHeightAboveMSL(const PointType& geoPoint);
 
+  /** Return the number of DEM opened */
   unsigned int GetDEMCount() const;
   
   itkGetMacro(DefaultHeightAboveEllipsoid, double);
   itkSetMacro(DefaultHeightAboveEllipsoid, double);
 
-  /** Get DEM directory */
+  /** Get DEM directory 
+   * \param idx directory index
+   * \return the DEM directory corresponding to index idx
+   */
   std::string GetDEMDirectory(unsigned int idx = 0) const;
 
   /** Get Geoid file */
   std::string GetGeoidFile() const;
 
+  /** Clear the DEM list and close all DEM datasets */
   void ClearDEMs();
 
 protected: 
   DEMHandler(); 
 
-  ~DEMHandler() = default;
+  ~DEMHandler();
 
-  boost::optional<double> GetDEMValue(double lon, double lat, GDALDataset* ds);
-  
-  void CreateShiftedDataset();
-  
-private:
 
 private:
   DEMHandler(const Self&) = delete;
@@ -97,15 +147,9 @@ private:
   /** Pointer to the DEM dataset */
   GDALDataset * m_Dataset;
 
-  GDALDataset* m_shiftedDS;
-  
   /** Pointer to the geoid dataset */
   GDALDataset* m_GeoidDS;
   
-  bool m_ApplyVerticalDatum;
-  
-  GDALRasterIOExtraArg m_RasterIOArgs;
-
   static Pointer m_Singleton;
 
   /** Default height above elliposid, used when no DEM or geoid height is available. */
diff --git a/Modules/IO/IOGDAL/src/otbDEMHandler.cxx b/Modules/IO/IOGDAL/src/otbDEMHandler.cxx
index 058fb3021837be36ec7fc546fdf1e656808c7b45..c855e282f3a63e8ce36a0065c28d6aaf43ecc265 100644
--- a/Modules/IO/IOGDAL/src/otbDEMHandler.cxx
+++ b/Modules/IO/IOGDAL/src/otbDEMHandler.cxx
@@ -4,8 +4,13 @@
 #include "boost/filesystem.hpp"
 #include "gdal_utils.h"
 
+//TODO C++ 17 : use std::optional instead
+#include <boost/optional.hpp>
 
 namespace otb {
+
+namespace DEMDetails {
+
 // Adapted from boost filesystem documentation : https://www.boost.org/doc/libs/1_53_0/libs/filesystem/doc/reference.html
 std::vector<std::string> GetFilesInDirectory(const std::string & directoryPath)
 {
@@ -37,6 +42,62 @@ std::vector<std::string> GetFilesInDirectory(const std::string & directoryPath)
   return fileList;
 }
 
+boost::optional<double> GetDEMValue(double lon, double lat, GDALDataset* ds)
+{
+  double geoTransform[6];
+  
+  ds->GetGeoTransform(geoTransform);
+
+  auto x = (lon - geoTransform[0]) / geoTransform[1] - 0.5;
+  auto y = (lat - geoTransform[3]) / geoTransform[5] - 0.5;
+
+  if (x < 0 || y < 0 || x > ds->GetRasterXSize() || y > ds->GetRasterYSize())
+  {
+    return boost::none;
+  }
+
+  auto x_int = static_cast<int>(x);
+  auto y_int = static_cast<int>(y);
+
+  auto deltaX = x - x_int;
+  auto deltaY = y - y_int;
+
+  if (x < 0 || y < 0 || x+1 > ds->GetRasterXSize() || y+1 > ds->GetRasterYSize())
+  {
+    return boost::none;
+  }
+  
+  // Bilinear interpolation.
+  double elevData[4];
+  
+  auto err = ds->GetRasterBand(1)->RasterIO( GF_Read, x_int, y_int, 2, 2,
+              elevData, 2, 2, GDT_Float64,
+              0, 0, nullptr);
+
+  if (err)
+  {
+    return boost::none;
+  }
+
+  // Test for no data. Don't return a value if one pixel 
+  // of the interpolation is no data.
+  for (int i =0; i<4; i++)
+  {
+    if (elevData[i] == ds->GetRasterBand(1)->GetNoDataValue())
+    {
+      return boost::none;
+    }
+  }
+
+  auto xBil1 = elevData[0] * (1-deltaX) + elevData[1] * deltaX;
+  auto xBil2 = elevData[2] * (1-deltaX) + elevData[3] * deltaX;
+
+  auto yBil = xBil1 * (1.0 - deltaY) + xBil2 * deltaY;
+  return yBil;
+}
+
+}  // namespace DEMDetails
+
 /** Initialize the singleton */
 DEMHandler::Pointer DEMHandler::m_Singleton = nullptr;
 
@@ -58,34 +119,28 @@ DEMHandler::Pointer DEMHandler::Instance()
 
 
 DEMHandler::DEMHandler() : m_Dataset(nullptr),
-                           m_shiftedDS(nullptr),
                            m_GeoidDS(nullptr),
-                           m_ApplyVerticalDatum(false), 
                            m_DefaultHeightAboveEllipsoid(0.0)
 {
   GDALAllRegister();
-  INIT_RASTERIO_EXTRA_ARG(m_RasterIOArgs);
-
-  //TODO : Setter for the resampling algorithm
-  // In OSSIM bilinear resampling is used
-  m_RasterIOArgs.eResampleAlg = GDALRIOResampleAlg::GRIORA_Bilinear;
 };
 
-void DEMHandler::OpenDEMFile(const std::string & path)
+DEMHandler::~DEMHandler()
+{
+  GDALClose(m_GeoidDS);
+  ClearDEMs();
+}
+
+void DEMHandler::OpenDEMFile(const std::string& path)
 {
   m_DatasetList.push_back(otb::GDALDriverManagerWrapper::GetInstance().Open(path));
   m_Dataset = m_DatasetList.back()->GetDataSet();
   m_DEMDirectories.push_back(path);
-  
-  if (m_GeoidDS)
-  {
-    CreateShiftedDataset();
-  }
 }
 
 void DEMHandler::OpenDEMDirectory(const std::string& DEMDirectory)
 {
-  auto demFiles = GetFilesInDirectory(DEMDirectory);
+  auto demFiles = DEMDetails::GetFilesInDirectory(DEMDirectory);
   for (const auto & file : demFiles)
   {
     auto ds = otb::GDALDriverManagerWrapper::GetInstance().Open(file);
@@ -125,173 +180,33 @@ void DEMHandler::OpenDEMDirectory(const std::string& DEMDirectory)
       delete[] vrtDatasetList;
     }
     
-    if (m_GeoidDS)
-    {
-      CreateShiftedDataset();
-    }
   }
 }
 
-void DEMHandler::OpenDEMDirectory(const char* DEMDirectory)
-{
-  OpenDEMDirectory(std::string(DEMDirectory));
-}
-
-boost::optional<double> DEMHandler::GetDEMValue(double lon, double lat, GDALDataset* ds)
-{
-  double value= 0.;
-  GDALRasterIOExtraArg extraArgs = m_RasterIOArgs;
-  
-  extraArgs.nVersion = 1;
-  
-  double geoTransform[6];
-  
-  ds->GetGeoTransform(geoTransform);
-
-  auto x = (lon - geoTransform[0]) / geoTransform[1] - 0.5;
-  auto y = (lat - geoTransform[3]) / geoTransform[5] - 0.5;
-
-  if (x < 0 || y < 0 || x > ds->GetRasterXSize() || y > ds->GetRasterYSize())
-  {
-    //std::cout << "invalid coordinates" << std::endl;
-    return boost::none;
-  }
-
-#if 0
-extraArgs.eResampleAlg = GDALRIOResampleAlg::GRIORA_Bilinear;
-  extraArgs.bFloatingPointWindowValidity = TRUE;
-  extraArgs.dfXOff = x;
-  extraArgs.dfYOff = y;
-  extraArgs.dfXSize = 1;
-  extraArgs.dfYSize = 1;
-
-  //ds->GetRasterBand(1)->GetNoDataValue() << std::endl;
-/*
-  auto err = ds->GetRasterBand(1)->RasterIO( GF_Read, static_cast<int>(x), static_cast<int>(y), 1, 1,
-              &value, 1, 1, GDT_Float64,
-              0, 0, &extraArgs);
-  */
-  auto err = ds->RasterIO( GF_Read, static_cast<int>(x), static_cast<int>(y), 1, 1,
-              &value, 1, 1, GDT_Float64, 1, nullptr,
-              0, 0,0, &extraArgs);
-  
-  if (err || value == ds->GetRasterBand(1)->GetNoDataValue())
-  {
-    return boost::none;
-  }
-  
-  //std::cout << "value :" << value << std::endl;
-  return value;
-
-#else
-  auto x_int = static_cast<int>(x);
-  auto y_int = static_cast<int>(y);
-
-  auto deltaX = x - x_int;
-  auto deltaY = y - y_int;
-/*
-  std::cout <<"x="<< x << " y=" << y << std::endl;
-  std::cout <<"x_int="<< x_int << " y_int=" << y_int << std::endl;
-  std::cout <<"deltaX="<< deltaX << " deltaY=" << deltaY << std::endl;
-  */
-  if (x < 0 || y < 0 || x+1 > ds->GetRasterXSize() || y+1 > ds->GetRasterYSize())
-  {
-    //std::cout << "invalid coordinates" << std::endl;
-    return boost::none;
-  }
-  
-  // Bilinear interpolation.
-  double elevData[4];
-  
-  auto err = ds->GetRasterBand(1)->RasterIO( GF_Read, x_int, y_int, 2, 2,
-              elevData, 2, 2, GDT_Float64,
-              0, 0, &extraArgs);
-
-  // Test for no data. Don't return a value if one pixel 
-  // of the interpolation is no data.
-  for (int i =0; i<4; i++)
+bool DEMHandler::OpenGeoidFile(const std::string& geoidFile)
+{  
+  if (m_GeoidDS)
   {
-    if (elevData[i] == ds->GetRasterBand(1)->GetNoDataValue())
-    {
-      return boost::none;
-    }
+    GDALClose(m_GeoidDS);
   }
 
-  auto xBil1 = elevData[0] * (1-deltaX) + elevData[1] * deltaX;
-  auto xBil2 = elevData[2] * (1-deltaX) + elevData[3] * deltaX;
-
-  auto yBil = xBil1 * (1.0 - deltaY) + xBil2 * deltaY;
-  return yBil; 
-  
-#endif
-}
-
-bool DEMHandler::OpenGeoidFile(const char* geoidFile)
-{
   int pbError;
 
-  m_GeoidDS = static_cast<GDALDataset*>(GDALOpenVerticalShiftGrid(geoidFile, &pbError));
+  m_GeoidDS = static_cast<GDALDataset*>(GDALOpenVerticalShiftGrid(geoidFile.c_str(), &pbError));
   m_GeoidFilename = geoidFile;
 
-  if (m_Dataset)
-  {
-    CreateShiftedDataset();
-  }
-}
-
-bool DEMHandler::OpenGeoidFile(const std::string& geoidFile)
-{
-  OpenGeoidFile(geoidFile.c_str());
-}
-
-void DEMHandler::CreateShiftedDataset()
-{
-  char **papszOptions = nullptr;
-  // Geoid resampling is bilinear in OSSIM
-  papszOptions = CSLSetNameValue( papszOptions, "RESAMPLING", "BILINEAR" ); // RESAMPLING=NEAREST/BILINEAR/CUBIC
-  papszOptions = CSLSetNameValue( papszOptions, "MAX_ERROR", "0" ); //MAX_ERROR=val
-  papszOptions = CSLSetNameValue( papszOptions, "DATATYPE", "Float64" ); //DATATYPE=Byte/UInt16/Int16/Float32/Float64
-  // ERROR_ON_MISSING_VERT_SHIFT=YES/NO
-  // SRC_SRS=srs_def 
-
-  m_shiftedDS = static_cast<GDALDataset*>( GDALApplyVerticalShiftGrid( m_Dataset,
-                                            m_GeoidDS,
-                                           false,
-                                           1.0,
-                                           1.0,
-                                           papszOptions ));
-
-  m_ApplyVerticalDatum = true;
+  return pbError;
 }
 
 double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat)
 {
-#if 0
-  auto ds = m_Dataset && m_GeoidDS ? m_shiftedDS
-           : m_Dataset ? m_Dataset
-           : m_GeoidDS ? m_GeoidDS
-           : nullptr;
-
-  if (ds)
-  {
-    auto result = GetDEMValue(lon, lat, ds);
-    if (result)
-    {
-      return *result;
-    }
-  }
-
-  return m_DefaultHeightAboveEllipsoid;
-#else
   double result = 0.;
   boost::optional<double> DEMresult;
   boost::optional<double> geoidResult;
 
-
-
   if (m_Dataset)
   {
-    DEMresult = GetDEMValue(lon, lat, m_Dataset);
+    DEMresult = DEMDetails::GetDEMValue(lon, lat, m_Dataset);
     if (DEMresult)
     {
       result += *DEMresult;
@@ -301,7 +216,7 @@ double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat)
 
   if (m_GeoidDS)
   {
-    geoidResult = GetDEMValue(lon, lat, m_GeoidDS);
+    geoidResult = DEMDetails::GetDEMValue(lon, lat, m_GeoidDS);
     if (geoidResult)
     {
       result += *geoidResult;
@@ -312,7 +227,6 @@ double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat)
     return result;
   else
     return m_DefaultHeightAboveEllipsoid;
-#endif
 }
 
 double DEMHandler::GetHeightAboveEllipsoid(const PointType& geoPoint)
@@ -324,7 +238,7 @@ double DEMHandler::GetHeightAboveMSL(double lon, double lat)
 {
   if (m_Dataset)
   { 
-    auto result = GetDEMValue(lon, lat, m_Dataset);
+    auto result = DEMDetails::GetDEMValue(lon, lat, m_Dataset);
     
     if (result)
     {
@@ -345,9 +259,9 @@ unsigned int DEMHandler::GetDEMCount() const
   return m_DatasetList.size();
 }
 
-bool DEMHandler::IsValidDEMDirectory(const char* DEMDirectory)
+bool DEMHandler::IsValidDEMDirectory(const std::string& DEMDirectory)
 {
-  for (const auto & filename : GetFilesInDirectory(DEMDirectory))
+  for (const auto & filename : DEMDetails::GetFilesInDirectory(DEMDirectory))
   {
     // test if a driver can identify this dataset
     auto identifyDriverH = static_cast<GDALDriver *>(GDALIdentifyDriver(filename.c_str(), nullptr));
@@ -363,9 +277,13 @@ bool DEMHandler::IsValidDEMDirectory(const char* DEMDirectory)
 std::string DEMHandler::GetDEMDirectory(unsigned int idx) const
 {
   if (idx >= m_DEMDirectories.size())
+  {
     return "";
+  }
   else
+  {
     return m_DEMDirectories[idx];
+  }
 }
 
 std::string DEMHandler::GetGeoidFile() const
@@ -377,7 +295,6 @@ void DEMHandler::ClearDEMs()
 {
   m_DEMDirectories.clear();
 
-  GDALClose(m_shiftedDS);
   // This will call GDALClose on all datasets
   m_DatasetList.clear();
 }