diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 25c72acc11ed237b4b04cfe55956b2a6e3354117..766ffcb44eaa7ccb634c8bb3148b2c71e9a5796d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -351,43 +351,43 @@ ubuntu-xdk-report:
     - job: ubuntu-xdk-qa-static-analysis
       artifacts: true
 
-#------------------------- Conda packages jobs ---------------------------------
-conda-linux-build:
-  extends: .common
-  stage: build
-  only:
-    - nightly
-    - /^release-[0-9]+\.[0-9]+$/@orfeotoolbox/otb
-  image: $BUILD_IMAGE_REGISTRY/otb-conda-build:latest
-  allow_failure: true
-  script:
-    - export otb_tag=${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}
-    - ./CI/conda_build.sh
-  after_script: []
-  needs:
-    - job: fast-build
-      artifacts: false
-  artifacts:
-    when: always
-    expire_in: 24 hrs
-    paths:
-      - conda-bld/*
+# #------------------------- Conda packages jobs ---------------------------------
+# conda-linux-build:
+#   extends: .common
+#   stage: build
+#   only:
+#     - nightly
+#     - /^release-[0-9]+\.[0-9]+$/@orfeotoolbox/otb
+#   image: $BUILD_IMAGE_REGISTRY/otb-conda-build:latest
+#   allow_failure: true
+#   script:
+#     - export otb_tag=${CI_COMMIT_TAG:-$CI_COMMIT_SHORT_SHA}
+#     - ./CI/conda_build.sh
+#   after_script: []
+#   needs:
+#     - job: fast-build
+#       artifacts: false
+#   artifacts:
+#     when: always
+#     expire_in: 24 hrs
+#     paths:
+#       - conda-bld/*
 
-conda-linux-deploy:
-  extends: .general
-  stage: deploy
-  only:
-    - /^release-[0-9]+\.[0-9]+$/@orfeotoolbox/otb
-  image: $BUILD_IMAGE_REGISTRY/otb-conda-build:latest
-  before_script:
-    # Provision efficiently the local LFS cache before checkout
-    - git lfs fetch origin $CI_COMMIT_SHA
-    - git checkout -f -q $CI_COMMIT_SHA
-  script:
-    - ./CI/conda_deploy.sh
-  needs:
-    - job: conda-linux-build
-      artifacts: yes
+# conda-linux-deploy:
+#   extends: .general
+#   stage: deploy
+#   only:
+#     - /^release-[0-9]+\.[0-9]+$/@orfeotoolbox/otb
+#   image: $BUILD_IMAGE_REGISTRY/otb-conda-build:latest
+#   before_script:
+#     # Provision efficiently the local LFS cache before checkout
+#     - git lfs fetch origin $CI_COMMIT_SHA
+#     - git checkout -f -q $CI_COMMIT_SHA
+#   script:
+#     - ./CI/conda_deploy.sh
+#   needs:
+#     - job: conda-linux-build
+#       artifacts: yes
 
 #---------------------------- Deploy job ---------------------------------------
 deploy:
diff --git a/.gitlab/issue_templates/documentation_changes.md b/.gitlab/issue_templates/documentation_changes.md
index d7189ba97f930404138940cb9d9bef4dcf7a963d..516ab7ab946eea7fc908ed9154390b6b2706b7fc 100644
--- a/.gitlab/issue_templates/documentation_changes.md
+++ b/.gitlab/issue_templates/documentation_changes.md
@@ -5,3 +5,5 @@ Can be a combination of CookBook, doxygen, blog, applications doc, websites
 ### Change requested
 
 Describe precisely the changes that are required.
+
+/label ~documentation 
diff --git a/.gitlab/issue_templates/release.md b/.gitlab/issue_templates/release.md
index fce450b8453dea9babc99fb33f604ed9faa10f89..7bf07303f9b8ae81e7ff0d17707916b7df7f70aa 100644
--- a/.gitlab/issue_templates/release.md
+++ b/.gitlab/issue_templates/release.md
@@ -1,5 +1,7 @@
 We are ready to release OTB version MAJOR.MINOR.PATCH. The following steps need to be done:
 
+## Release Candidate
+
 ### 1. Branches
 
 * [ ] **(if major or minor release)** Feature freeze: [create the new release branch](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#create-release-branch)
@@ -8,21 +10,58 @@ We are ready to release OTB version MAJOR.MINOR.PATCH. The following steps need
 
 ### 2. Housekeeping
 * [ ] In this story, make a list of blocking issues for the release (if any)
-* [ ] [Update the SuperBuild archive](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#superbuild-archive) (if needed)
 * [ ] Update release notes (walk the GitLab MR merged history and log all improvements)
 * [ ] Update the date in RELEASE_NOTES.txt
+* [ ] Check [SonarQube](https://sonar.orfeo-toolbox.org/dashboard?id=orfeotoolbox-otb)
+* [ ] Run Debian [spelling](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#spelling-check) checker
+* [ ] Run shellcheck script from [OTB-Devutils/Scripts/](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb-devutils/blob/master/Scripts/run_shellcheck.sh)
+* [ ] [Update translation](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#translation-for-monteverdi-mapla) for Monteverdi and Mapla
+* [ ] [Sanity check the binary packages](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#standalone-packages-sanity-check)
+    * [ ] Windows
+    * [ ] Linux
+    * [ ] Mac
+    * [ ] Test QGIS on qgis docker image
+
+### 3. Actual release
+
+Once all blocking issues are closed, and the previous steps are done:
+* [ ] [Tag the release candidate](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#release-tag)
+* [ ] Update GIT_TAG for all official remote modules (if needed)
+
+### 4. Publish and plan next release
+* [ ]   [Prepare and upload source packages](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#prepare-and-upload-source-packages)
+* [ ]   [Promote staging packages](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#promote-staging-packages)
+* [ ]   [Update documentation](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#update-documentation)
+    * [ ] Cookbook
+    * [ ] Doxygen
+* [ ] [Update the SuperBuild archive](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#superbuild-archive)
+* [ ] Release Candidate announcement on the forum
+
+## Release
+
+### 1. Branches
+
+* [ ] Make sure the version number in `CMakeLists.txt` is MAJOR.MINOR.PATCH
+
+### 2. Housekeeping
+* [ ] In this story, make a list of blocking issues for the release (if any)
+* [ ] Fix compilation warnings on CI
+* [ ] Update release notes (walk the GitLab MR merged history and log all improvements)
+* [ ] Update the date in RELEASE_NOTES.txt
+* [ ] Check [SonarQube](https://sonar.orfeo-toolbox.org/dashboard?id=orfeotoolbox-otb)
 * [ ] Run Debian [spelling](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#spelling-check) checker
 * [ ] Run shellcheck script from [OTB-Devutils/Scripts/](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb-devutils/blob/master/Scripts/run_shellcheck.sh)
 * [ ] [Update translation](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#translation-for-monteverdi-mapla) for Monteverdi and Mapla
 * [ ] [Sanity check the binary packages](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#standalone-packages-sanity-check)
-    * [ ]  Windows
-    * [ ]  Linux
-    * [ ]  Mac
+    * [ ] Windows
+    * [ ] Linux
+    * [ ] Mac
+    * [ ] Test QGIS on qgis docker image
 
 ### 3. Actual release
 
 Once all blocking issues are closed, and the previous steps are done:
-* [ ] [Tag the release or RC](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#release-tag)
+* [ ] [Tag the release](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#release-tag)
 * [ ] **(if major or minor release)**: Merge the release into develop
 * [ ] **(if it's the latest release)**: Merge the release into master
 * [ ] **(if patch release)**: Backport fixes
@@ -36,11 +75,12 @@ Once all blocking issues are closed, and the previous steps are done:
     * [ ] Doxygen
     * [ ] WordPress page "Home" and "Download" pages
 * [ ] Upload OTB source archive to [Zenodo](https://zenodo.org/) to create a unique Digital Object Identifier (DOI)
-* [ ] Send email to mailing list to announce the release
-* [ ] Release announcement on the blog
-* [ ] Announcement on social networks (twitter, google+)
+* [ ] [Update the SuperBuild archive](https://gitlab.orfeo-toolbox.org/orfeotoolbox/otb/wikis/Help-for-release-actions#superbuild-archive)
+* [ ] Release announcement
+    * [ ] On the [forum](https://forum.orfeo-toolbox.org/)
+    * [ ] On the [blog](https://www.orfeo-toolbox.org/blog/)
+	* [ ] On [Twitter](https://twitter.com/orfeotoolbox)
 * [ ] Forward announcement to news_item@osgeo.org ([OSGeo news](https://www.osgeo.org/foundation-news/))
-* [ ] Plan the next release (nominate new release manager, setup PSC meeting on IRC)
 * [ ] Remove public branches related to MR or bugfix merged before the release
 
 /label ~story
diff --git a/.mailmap b/.mailmap
index ddb74cbf386ca0365db492ffc9f2739b8e155cca..bd42eb271375706fd84d5da83fa1ec6aac743add 100644
--- a/.mailmap
+++ b/.mailmap
@@ -12,6 +12,7 @@ Aurélie Emilien <aurelie.emilien@c-s.fr>
 Aurélie Emilien <aurelie.emilien@c-s.fr>                  Aurelie Emilien <aurelie.emilien@c-s.fr>
 Bas Couwenberg <sebastic@debian.org>
 Bas Couwenberg <sebastic@debian.org>                      Bas Couwenberg <sebastic@xs4all.nl>
+Brad Hards <bradh@frogmouth.net>
 Carole Amiot <carole.amiot@cnes.fr>
 Carole Amiot <carole.amiot@cnes.fr>                       Carole Amiot <carole.amiot@thales-services.fr>
 Caroline Ruffel <caroline.ruffel@c-s.fr>
@@ -58,7 +59,8 @@ Julien Michel <julien.michel@cnes.fr>                     Julien Michel <julien.
 Julien Osman <julien.osman@csgroup.eu>                    Julien Osman <julien.osman@c-s.fr>
 Laurențiu Nicola <lnicola@dend.ro>                        Laurentiu Nicola <lnicola@dend.ro>
 Laurențiu Nicola <lnicola@dend.ro>                        Laurențiu Nicola <grayshade@gmail.com>
-Luc Hermitte <luc.hermitte@c-s.fr>                        Luc Hermitte <luc.hermitte@cnes.fr>
+Luc Hermitte <luc.hermitte@csgroup.eu>                    Luc Hermitte <luc.hermitte@cnes.fr>
+Luc Hermitte <luc.hermitte@csgroup.eu>                    Luc Hermitte <luc.hermitte@c-s.fr>
 Ludovic Hussonnois <ludovic.hussonnois@c-s.fr>
 Manuel Grizonnet <manuel.grizonnet@cnes.fr>
 Manuel Grizonnet <manuel.grizonnet@cnes.fr>               Grizonnet Manuel <manuel.grizonnet@cnes.fr>
diff --git a/CI/configure_options.cmake b/CI/configure_options.cmake
index 25a271891b89603e51890ff0530f7c49369d9d9c..2f10ee0e995d0e8fe4e6a8430ee96701fef5c849 100644
--- a/CI/configure_options.cmake
+++ b/CI/configure_options.cmake
@@ -70,7 +70,7 @@ if(XDK_PATH)
 set(cmake_configure_option
 "${cmake_configure_option}
 CMAKE_PREFIX_PATH=${XDK_PATH}")
-foreach(remote_module OTBTemporalGapFilling SertitObject otbGRM DiapOTBModule)
+foreach(remote_module OTBTemporalGapFilling SertitObject otbGRM DiapOTBModule S1TilingSupportApplications)
   set(cmake_configure_option
 "${cmake_configure_option}
 Module_${remote_module}:BOOL=ON")
diff --git a/CMake/GetVersionFromGitTag.cmake b/CMake/GetVersionFromGitTag.cmake
index 331635c2bdacd5c0661082a7e23ace31371c9db0..0b6c5804d2456184a6b5ea34c85f89277c8fffae 100644
--- a/CMake/GetVersionFromGitTag.cmake
+++ b/CMake/GetVersionFromGitTag.cmake
@@ -1,3 +1,26 @@
+#
+# Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+#
+# This file is part of Orfeo Toolbox
+#
+#     https://www.orfeo-toolbox.org/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# This cmake module is based on a module written by Nuno Fachada, below is the
+# original header of the module:
+
 #
 # This cmake module sets the project version and partial version
 # variables by analysing the git tag and commit history. It expects git
@@ -32,7 +55,7 @@
 # Author: Nuno Fachada
 
 
-function(get_package_name root_repo_dir project_version_string)
+function(get_package_name root_repo_dir project project_version_string)
   
   if(EXISTS "${root_repo_dir}/.git")  
     find_package(Git)
@@ -40,12 +63,6 @@ function(get_package_name root_repo_dir project_version_string)
       message(ERROR "git not found. Make sure git can be found in your PATH")
       return()
     endif()
-    
-    message(STATUS "PROJECT_NAME: ${PROJECT_NAME}")
-    message(STATUS "VERSION MINOR: ${${PROJECT_NAME}_VERSION_MAJOR}")
-    message(STATUS "VERSION MAJOR: ${${PROJECT_NAME}_VERSION_MINOR}")
-    message(STATUS "VERSION PATCH: ${${PROJECT_NAME}_VERSION_PATCH}")
-
 
     if(DEFINED ENV{CI_COMMIT_REF_NAME})
       set(branch_name "$ENV{CI_COMMIT_REF_NAME}")
@@ -65,7 +82,7 @@ function(get_package_name root_repo_dir project_version_string)
 
     if("${branch_name}" MATCHES "^release-[0-9]+\\.[0-9]+\$")
 
-      set(${project_version_string} "${${PROJECT_NAME}_VERSION_MAJOR}.${${PROJECT_NAME}_VERSION_MINOR}.${${PROJECT_NAME}_VERSION_PATCH}" PARENT_SCOPE)
+      set(${project_version_string} "${${project}_VERSION_MAJOR}.${${project}_VERSION_MINOR}.${${project}_VERSION_PATCH}" PARENT_SCOPE)
 
     else()
       if(DEFINED ENV{CI_COMMIT_SHORT_SHA})
@@ -73,9 +90,9 @@ function(get_package_name root_repo_dir project_version_string)
       else()
         execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
           WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
-          OUTPUT_VARIABLE ${PROJECT_NAME}_COMMIT_SHA_STRING
+          OUTPUT_VARIABLE ${project}_COMMIT_SHA_STRING
           OUTPUT_STRIP_TRAILING_WHITESPACE)
-        set(${project_version_string} "${branch_name}-${${PROJECT_NAME}_COMMIT_SHA_STRING}" PARENT_SCOPE)
+        set(${project_version_string} "${branch_name}-${${project}_COMMIT_SHA_STRING}" PARENT_SCOPE)
       endif()
 
     endif()
@@ -99,7 +116,7 @@ function(get_package_name root_repo_dir project_version_string)
     
     message(STATUS "M: ${_VERSION_MAJOR}, m: ${_VERSION_MINOR}, p: ${_VERSION_PATCH}")
 
-	  set(${project_version_string} "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
+    set(${project_version_string} "${PROJECT_VERSION_STRING}" PARENT_SCOPE)
     
   endif()
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c8df73beca3627a8fca350c2c0e1d672dd271a38..a47074959c402d9f1b0592c82b45618b256017b2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -133,14 +133,11 @@ set(main_project_name ${_OTBModuleMacros_DEFAULT_LABEL})
 #-----------------------------------------------------------------------------
 # OTB version number.
 set(OTB_VERSION_MAJOR "7")
-set(OTB_VERSION_MINOR "1")
+set(OTB_VERSION_MINOR "2")
 set(OTB_VERSION_PATCH "0")
 set(OTB_VERSION_STRING "${OTB_VERSION_MAJOR}.${OTB_VERSION_MINOR}.${OTB_VERSION_PATCH}")
 
-get_package_name(${OTB_SOURCE_DIR} OTB_VERSION_STRING2)
-
-message(STATUS "## ${OTB_VERSION_STRING2} ##")
-
+get_package_name(${OTB_SOURCE_DIR} ${PROJECT_NAME} OTB_VERSION_STRING2)
 
 # Monteverdi version number (follows OTB)
 set( Monteverdi_VERSION_MAJOR ${OTB_VERSION_MAJOR} )
diff --git a/Data/Baseline/OTB-Applications/Images/ResetMarginBaseline100x100.tiff b/Data/Baseline/OTB-Applications/Images/ResetMarginBaseline100x100.tiff
new file mode 100644
index 0000000000000000000000000000000000000000..d687d442d20c144ca775fc32122e54b49b28cb8e
--- /dev/null
+++ b/Data/Baseline/OTB-Applications/Images/ResetMarginBaseline100x100.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2c8358d04f4d40cdbf08c836bba4ff725c7f8d1d0daeef8c2d336090a240c928
+size 33041
diff --git a/Data/Baseline/OTB-Applications/Images/s1a_33NWB_vv_DES_007_20200108txxxxxx_100x100.tif b/Data/Baseline/OTB-Applications/Images/s1a_33NWB_vv_DES_007_20200108txxxxxx_100x100.tif
new file mode 100644
index 0000000000000000000000000000000000000000..59d9650a2bcb8118befd5086d260404eb043aa20
--- /dev/null
+++ b/Data/Baseline/OTB-Applications/Images/s1a_33NWB_vv_DES_007_20200108txxxxxx_100x100.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c1b49117a90f751d70f80f1c79975a6a45fbd278d349e2e77844050f641774c5
+size 34870
diff --git a/Data/Baseline/OTB/Images/apTvUtQuicklookWithGCP.1.tif b/Data/Baseline/OTB/Images/apTvUtQuicklookWithGCP.1.tif
new file mode 100644
index 0000000000000000000000000000000000000000..75d4e4f6a4ad8aed07ee9bdd04f8e5cc79e622d9
--- /dev/null
+++ b/Data/Baseline/OTB/Images/apTvUtQuicklookWithGCP.1.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d4022ef746f67015198c6e43952085ad483ec3608eefb241360a58d241d5c3ed
+size 10375
diff --git a/Data/Baseline/OTB/Images/ioTvDEMToImageGenerator2.1.tif b/Data/Baseline/OTB/Images/ioTvDEMToImageGenerator2.1.tif
new file mode 100644
index 0000000000000000000000000000000000000000..a18a25e6cde1c5497ead7a587829d1710204598e
--- /dev/null
+++ b/Data/Baseline/OTB/Images/ioTvDEMToImageGenerator2.1.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ff13c38236ad2493382197314c218f43413dd16cc3e817d829237d7db3ac41fd
+size 29379
diff --git a/Data/Input/DEM/egm96.grd.hdr b/Data/Input/DEM/egm96.grd.hdr
new file mode 100644
index 0000000000000000000000000000000000000000..b20f998535b4072e9e9e972bbb7c7cebc129a54f
--- /dev/null
+++ b/Data/Input/DEM/egm96.grd.hdr
@@ -0,0 +1,16 @@
+ENVI
+samples = 1441
+lines   = 721
+bands   = 1
+header offset = 24
+file type = ENVI Standard
+data type = 4
+interleave = bsq
+sensor type = Unknown
+byte order = 1
+wavelength units = Unknown
+map info = {Geographic Lat/Lon, 1, 1,-0.125, 90.125, 0.25, 0.25,WGS-84}
+coordinate system string = {GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]}
+band names = {
+Band 1}
+
diff --git a/Data/Input/DEM_srtm/egm96.grd.hdr b/Data/Input/DEM_srtm/egm96.grd.hdr
new file mode 100644
index 0000000000000000000000000000000000000000..b20f998535b4072e9e9e972bbb7c7cebc129a54f
--- /dev/null
+++ b/Data/Input/DEM_srtm/egm96.grd.hdr
@@ -0,0 +1,16 @@
+ENVI
+samples = 1441
+lines   = 721
+bands   = 1
+header offset = 24
+file type = ENVI Standard
+data type = 4
+interleave = bsq
+sensor type = Unknown
+byte order = 1
+wavelength units = Unknown
+map info = {Geographic Lat/Lon, 1, 1,-0.125, 90.125, 0.25, 0.25,WGS-84}
+coordinate system string = {GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137,298.257223563]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]]}
+band names = {
+Band 1}
+
diff --git a/Data/Input/ResetMarginInput100x100.tiff b/Data/Input/ResetMarginInput100x100.tiff
new file mode 100644
index 0000000000000000000000000000000000000000..ee84e4b93e759fea4c9dcd6d686ca45bf3277231
--- /dev/null
+++ b/Data/Input/ResetMarginInput100x100.tiff
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a1354f27b0141d6e7c58fcce825e4f988dc135aeed0a45a051aeb658c6bcf653
+size 50506
diff --git a/Data/Input/s1a_33NWB_vv_DES_007_20200108t044150_100x100.tif b/Data/Input/s1a_33NWB_vv_DES_007_20200108t044150_100x100.tif
new file mode 100644
index 0000000000000000000000000000000000000000..4d4e02c34b593dc88073746cf5beb77a285832c6
--- /dev/null
+++ b/Data/Input/s1a_33NWB_vv_DES_007_20200108t044150_100x100.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:99bde32908be865a70b59524ad0ca0e984490a1d46b4675d714e740209dba608
+size 19628
diff --git a/Data/Input/s1a_33NWB_vv_DES_007_20200108t044215_100x100.tif b/Data/Input/s1a_33NWB_vv_DES_007_20200108t044215_100x100.tif
new file mode 100644
index 0000000000000000000000000000000000000000..23e2b6bd7248c933144770a5ce760844430a5365
--- /dev/null
+++ b/Data/Input/s1a_33NWB_vv_DES_007_20200108t044215_100x100.tif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53a6a9e06b772c692f581117289168b14f077fab863280202a1bc9a9160e6043
+size 16763
diff --git a/Documentation/Cookbook/_static/html/versions.html b/Documentation/Cookbook/_static/html/versions.html
index d9c2deb6067af73b839f13eb0846f730e9213b37..dc6c5f6161d777d3d86be037aaed64995b33cd5c 100644
--- a/Documentation/Cookbook/_static/html/versions.html
+++ b/Documentation/Cookbook/_static/html/versions.html
@@ -12,6 +12,7 @@
             <dd><a href="https://www.orfeo-toolbox.org/CookBook-6.6.1/">6.6.1</a></dd>
             <dd><a href="https://www.orfeo-toolbox.org/CookBook-7.0/">7.0.0</a></dd>
             <dd><a href="https://www.orfeo-toolbox.org/CookBook-7.1/">7.1.0</a></dd>
+            <dd><a href="https://www.orfeo-toolbox.org/CookBook-7.2/">7.2.0</a></dd>
             <dd><a href="https://www.orfeo-toolbox.org/CookBook-develop/">develop</a></dd>
         </dl>
     </div>
diff --git a/Documentation/Cookbook/rst/Monteverdi.rst b/Documentation/Cookbook/rst/Monteverdi.rst
index f2ca1637aadf30fc481bcbcd5c33d53bd5ad5cf5..be875dca7dee320a8895a1167324f1e5f40464cc 100644
--- a/Documentation/Cookbook/rst/Monteverdi.rst
+++ b/Documentation/Cookbook/rst/Monteverdi.rst
@@ -235,7 +235,7 @@ placed exactly at the same position in the screenshots).
 .. _BM:
 .. figure:: Art/MonteverdiImages/BM.png
 
-:numref:`BM`: Comparision of histograms before and after applying BandMath.
+:numref:`BM`: Comparison of histograms before and after applying BandMath.
 
 Segmentation
 ~~~~~~~~~~~~
diff --git a/Examples/BasicFilters/DEMToRainbowExample.cxx b/Examples/BasicFilters/DEMToRainbowExample.cxx
index 1a90810bc0bc7d49c7ee78d7f25de24c381138ca..f85521c06e7ce8a31810d39667112775612bc2f0 100644
--- a/Examples/BasicFilters/DEMToRainbowExample.cxx
+++ b/Examples/BasicFilters/DEMToRainbowExample.cxx
@@ -20,17 +20,17 @@
 
 
 /* Example usage:
-./DEMToRainbowExample Output/DEMToRainbowImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm
+./DEMToRainbowExample Output/DEMToRainbowImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM/srtm_directory_srtm
 */
 
 
 /* Example usage:
-./DEMToRainbowExample Output/DEMToHotImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm hot
+./DEMToRainbowExample Output/DEMToHotImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM/srtm_directory hot
 */
 
 
 /* Example usage:
-./DEMToRainbowExample Output/DEMToReliefImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm relief
+./DEMToRainbowExample Output/DEMToReliefImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM/srtm_directory relief
 */
 
 #include "otbImageFileReader.h"
@@ -70,7 +70,7 @@ int main(int argc, char* argv[])
   using SpacingType = DEMToImageGeneratorType::SpacingType;
   using PointType   = DEMToImageGeneratorType::PointType;
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(argv[8]);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[8]);
 
   PointType origin;
   origin[0] = ::atof(argv[2]);
diff --git a/Examples/BasicFilters/HillShadingExample.cxx b/Examples/BasicFilters/HillShadingExample.cxx
index aa57e7be5e7e3d27c8edb51853ca9a8460a874ae..4109db8209ecdd1cf387418dc3e48b71aaea2385 100644
--- a/Examples/BasicFilters/HillShadingExample.cxx
+++ b/Examples/BasicFilters/HillShadingExample.cxx
@@ -20,7 +20,7 @@
 
 
 /* Example usage:
-./HillShadingExample Output/HillShadingExample.png Output/HillShadingColorExample.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm
+./HillShadingExample Output/HillShadingExample.png Output/HillShadingColorExample.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM/srtm_directory
 */
 
 
@@ -71,7 +71,7 @@ int main(int argc, char* argv[])
   using SpacingType = DEMToImageGeneratorType::SpacingType;
   using PointType   = DEMToImageGeneratorType::PointType;
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(argv[9]);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[9]);
 
   PointType origin;
   origin[0] = ::atof(argv[3]);
diff --git a/Examples/BasicFilters/test/CMakeLists.txt b/Examples/BasicFilters/test/CMakeLists.txt
index 777743508dd9f0941ceb94f010d8ec2bd9d1fbe4..76e981391f518efad6482285b7588092e8c21985 100644
--- a/Examples/BasicFilters/test/CMakeLists.txt
+++ b/Examples/BasicFilters/test/CMakeLists.txt
@@ -51,7 +51,7 @@ otb_add_test(NAME bfTeDEMToRainbowExampleTest COMMAND ${OTB_TEST_DRIVER}
     ${TEMP}/DEMToRainbowImageGenerator.png
   Execute $<TARGET_FILE:DEMToRainbowExample>
     ${TEMP}/DEMToRainbowImageGenerator.png
-    6.5 45.5 500 500 0.002 -0.002 ${OTB_DATA_ROOT}/Input/DEM_srtm
+    6.5 45.5 500 500 0.002 -0.002 ${OTB_DATA_ROOT}/Input/DEM/srtm_directory
 )
 
 
@@ -139,5 +139,5 @@ otb_add_test(NAME bfTeHillShadingExampleTest COMMAND ${OTB_TEST_DRIVER}
   Execute $<TARGET_FILE:HillShadingExample>
     ${TEMP}/HillShadingExample.png
     ${TEMP}/HillShadingColorExample.png
-    6.8 45.3 500 500 0.002 -0.002 ${OTB_DATA_ROOT}/Input/DEM_srtm
+    6.8 45.3 500 500 0.002 -0.002 ${OTB_DATA_ROOT}/Input/DEM/srtm_directory
 )
diff --git a/Examples/DisparityMap/StereoReconstructionExample.cxx b/Examples/DisparityMap/StereoReconstructionExample.cxx
index 72ac03ab56d8403baaaa62053364fd1ef6e40bd2..9a4292397a99623acf7954b7f97ea2d47821fd5c 100644
--- a/Examples/DisparityMap/StereoReconstructionExample.cxx
+++ b/Examples/DisparityMap/StereoReconstructionExample.cxx
@@ -114,7 +114,7 @@ int main(int argc, char* argv[])
   using DisparityToElevationFilterType = otb::DisparityMapToDEMFilter<FloatImageType, FloatImageType, FloatImageType, FloatVectorImageType, FloatImageType>;
 
   double avgElevation = atof(argv[5]);
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(avgElevation);
+  otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(avgElevation);
 
   ImageReaderType::Pointer leftReader  = ImageReaderType::New();
   ImageReaderType::Pointer rightReader = ImageReaderType::New();
diff --git a/Examples/IO/DEMHandlerExample.cxx b/Examples/IO/DEMHandlerExample.cxx
index 85df83d955f83fe2ade25cae15ae808591854160..8f7dd67226259b6e23d5c6b49ce0e5034403d606 100644
--- a/Examples/IO/DEMHandlerExample.cxx
+++ b/Examples/IO/DEMHandlerExample.cxx
@@ -54,7 +54,7 @@ int main(int argc, char* argv[])
   // This class is a singleton, the New() method is deprecated and will be removed
   // in future release. We need to use the \code{Instance()} method instead.
 
-  otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
+  auto & demHandler = otb::DEMHandler::GetInstance();
 
   bool fail = false;
 
@@ -63,16 +63,16 @@ int main(int argc, char* argv[])
   // allows inputting a geoid file as well. Last, a default height above ellipsoid
   // can be set using the \code{SetDefaultHeightAboveEllipsoid()} method.
 
-  demHandler->SetDefaultHeightAboveEllipsoid(defaultHeight);
+  demHandler.SetDefaultHeightAboveEllipsoid(defaultHeight);
 
-  if (!demHandler->IsValidDEMDirectory(demdir.c_str()))
+  if (!demHandler.IsValidDEMDirectory(demdir.c_str()))
   {
     std::cerr << "IsValidDEMDirectory(" << demdir << ") = false" << std::endl;
     fail = true;
   }
 
-  demHandler->OpenDEMDirectory(demdir);
-  demHandler->OpenGeoidFile(geoid);
+  demHandler.OpenDEMDirectory(demdir);
+  demHandler.OpenGeoidFile(geoid);
 
   // We can now retrieve height above ellipsoid or height above Mean Sea Level
   // (MSL) using the methods \code{GetHeightAboveEllipsoid()} and
@@ -104,10 +104,10 @@ int main(int argc, char* argv[])
 
   double height = -32768;
 
-  height = demHandler->GetHeightAboveMSL(point);
+  height = demHandler.GetHeightAboveMSL(point);
   std::cout << "height above MSL (" << longitude << "," << latitude << ") = " << height << " meters" << std::endl;
 
-  height = demHandler->GetHeightAboveEllipsoid(point);
+  height = demHandler.GetHeightAboveEllipsoid(point);
   std::cout << "height above ellipsoid (" << longitude << ", " << latitude << ") = " << height << " meters" << std::endl;
 
   // Note that OSSIM internal calls for sensor
diff --git a/Examples/IO/DEMToImageGenerator.cxx b/Examples/IO/DEMToImageGenerator.cxx
index 424d990e9ae5b760c02c5bd8051f8b12fcaf3536..4dc391e3b67fa8e8cca2fcd3df87d62dfda7de13 100644
--- a/Examples/IO/DEMToImageGenerator.cxx
+++ b/Examples/IO/DEMToImageGenerator.cxx
@@ -20,7 +20,7 @@
 
 
 /* Example usage:
-./DEMToImageGenerator Output/DEMToImageGenerator.tif Output/pretty_DEMToImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM_srtm
+./DEMToImageGenerator Output/DEMToImageGenerator.tif Output/pretty_DEMToImageGenerator.png 6.5 45.5 500 500 0.002 -0.002 Input/DEM/srtm_directory
 */
 
 
@@ -83,7 +83,7 @@ int main(int argc, char* argv[])
   WriterType::Pointer writer = WriterType::New();
 
   // The path to the DEM folder is given to the \doxygen{otb}{DEMHandler}.
-  otb::DEMHandler::Instance()->OpenDEMDirectory(folderPath);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(folderPath);
 
   // The origin (Longitude/Latitude) of the output image in the DEM is given to the filter.
   PointType origin;
diff --git a/Examples/IO/test/CMakeLists.txt b/Examples/IO/test/CMakeLists.txt
index e4ece80fe2e2c12f555e6e782fa7694ea62c138a..bcd30085bd207f23dc5cc629899e2734aed3c76b 100644
--- a/Examples/IO/test/CMakeLists.txt
+++ b/Examples/IO/test/CMakeLists.txt
@@ -49,13 +49,13 @@ otb_add_test(NAME ioTeDEMToImageGeneratorTest COMMAND ${OTB_TEST_DRIVER}
     500
     0.002
     -0.002
-    ${INPUTDATA}/DEM_srtm
+    ${INPUTDATA}/DEM/srtm_directory
 )
 
 otb_add_test(NAME prTeDEMHandlerExampleTest COMMAND ${OTB_TEST_DRIVER}
   Execute $<TARGET_FILE:DEMHandlerExample>
-    ${INPUTDATA}/DEM_srtm
-    ${INPUTDATA}/DEM_srtm/egm96.grd
+    ${INPUTDATA}/DEM/srtm_directory
+    ${INPUTDATA}/DEM/egm96.grd
     40
     8.434583
     44.647083
diff --git a/Modules/Adapters/OSSIMAdapters/include/otbDEMHandler.h b/Modules/Adapters/OSSIMAdapters/include/otbOssimDEMHandler.h
similarity index 94%
rename from Modules/Adapters/OSSIMAdapters/include/otbDEMHandler.h
rename to Modules/Adapters/OSSIMAdapters/include/otbOssimDEMHandler.h
index 6dd03da5d0bc670f14f90eec165182ba79ccdb31..16a4002e047af30be6cdc0225fe5490c49946ba4 100644
--- a/Modules/Adapters/OSSIMAdapters/include/otbDEMHandler.h
+++ b/Modules/Adapters/OSSIMAdapters/include/otbOssimDEMHandler.h
@@ -18,8 +18,8 @@
  * limitations under the License.
  */
 
-#ifndef otbDEMHandler_h
-#define otbDEMHandler_h
+#ifndef otbOssimDEMHandler_h
+#define otbOssimDEMHandler_h
 
 #include <cstdio>
 
@@ -35,14 +35,14 @@ class ossimElevManager;
 
 namespace otb
 {
-/** \class DEMHandler
+/** \class OssimDEMHandler
  *
  * \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.
+ * OssimDEMHandler::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
@@ -87,11 +87,11 @@ namespace otb
  * \ingroup OTBOSSIMAdapters
  */
 
-class OTBOSSIMAdapters_EXPORT DEMHandler : public itk::Object
+class OTBOSSIMAdapters_EXPORT OssimDEMHandler : public itk::Object
 {
 public:
   /** Standard class typedefs. */
-  typedef DEMHandler                    Self;
+  typedef OssimDEMHandler                    Self;
   typedef itk::Object                   Superclass;
   typedef itk::SmartPointer<Self>       Pointer;
   typedef itk::SmartPointer<const Self> ConstPointer;
@@ -102,7 +102,7 @@ public:
   static Pointer Instance();
 
   /** Run-time type information (and related methods). */
-  itkTypeMacro(DEMHandler, Object);
+  itkTypeMacro(OssimDEMHandler, Object);
 
   /** Try to open the DEM directory. */
   virtual void OpenDEMDirectory(const char* DEMDirectory);
@@ -168,8 +168,8 @@ public:
   void ClearDEMs();
 
 protected:
-  DEMHandler();
-  ~DEMHandler() override
+  OssimDEMHandler();
+  ~OssimDEMHandler() override
   {
   }
 
diff --git a/Modules/Adapters/OSSIMAdapters/otb-module.cmake b/Modules/Adapters/OSSIMAdapters/otb-module.cmake
index 346059b6ae76af48c428a79580aaf415e12fcbab..12a161c538fe4b5928ff8b5534b6248b8ac4fccd 100644
--- a/Modules/Adapters/OSSIMAdapters/otb-module.cmake
+++ b/Modules/Adapters/OSSIMAdapters/otb-module.cmake
@@ -32,13 +32,14 @@ ENABLE_SHARED
     OTBGDAL
     OTBOssimPlugins
     OTBOssim
-
+    
   TEST_DEPENDS
     OTBTestKernel
     OTBImageBase
     OTBImageIO
     OTBTransform
     OTBProjection
+    OTBIOGDAL
 
   DESCRIPTION
     "${DOCUMENTATION}"
diff --git a/Modules/Adapters/OSSIMAdapters/src/CMakeLists.txt b/Modules/Adapters/OSSIMAdapters/src/CMakeLists.txt
index a12d2b6efcbb6c3ad743d06b588433c511915533..dee3b08236cfe6eaa5ee3bf431f7534f7dacabaa 100644
--- a/Modules/Adapters/OSSIMAdapters/src/CMakeLists.txt
+++ b/Modules/Adapters/OSSIMAdapters/src/CMakeLists.txt
@@ -19,13 +19,11 @@
 #
 
 set(OTBOSSIMAdapters_SRC
-  otbDEMHandler.cxx
+  otbOssimDEMHandler.cxx
   otbImageKeywordlist.cxx
-  otbSensorModelAdapter.cxx
   otbRPCSolverAdapter.cxx
   otbDateTimeAdapter.cxx
   otbEllipsoidAdapter.cxx
-  otbSarSensorModelAdapter.cxx
   )
 
 add_library(OTBOSSIMAdapters ${OTBOSSIMAdapters_SRC})
diff --git a/Modules/Adapters/OSSIMAdapters/src/otbImageKeywordlist.cxx b/Modules/Adapters/OSSIMAdapters/src/otbImageKeywordlist.cxx
index b3d0889e7834ed8da68880f8419dafb8b50ada4d..0f20e1bd8b27c9baf179f9bd88f671310ece5ccd 100644
--- a/Modules/Adapters/OSSIMAdapters/src/otbImageKeywordlist.cxx
+++ b/Modules/Adapters/OSSIMAdapters/src/otbImageKeywordlist.cxx
@@ -57,7 +57,7 @@
 #include "ossim/base/ossimNotify.h"
 #endif
 
-#include "otbSensorModelAdapter.h"
+//#include "otbSensorModelAdapter.h"
 #include <memory>
 #include <boost/scoped_ptr.hpp>
 #include <boost/algorithm/string/predicate.hpp>
diff --git a/Modules/Adapters/OSSIMAdapters/src/otbDEMHandler.cxx b/Modules/Adapters/OSSIMAdapters/src/otbOssimDEMHandler.cxx
similarity index 82%
rename from Modules/Adapters/OSSIMAdapters/src/otbDEMHandler.cxx
rename to Modules/Adapters/OSSIMAdapters/src/otbOssimDEMHandler.cxx
index 92c9ce40b3f46d19dcd534e4d688a3bd6d3a4c04..fd318266ed5bce5468c1ab94e99114b5605ded93 100644
--- a/Modules/Adapters/OSSIMAdapters/src/otbDEMHandler.cxx
+++ b/Modules/Adapters/OSSIMAdapters/src/otbOssimDEMHandler.cxx
@@ -18,7 +18,7 @@
  * limitations under the License.
  */
 
-#include "otbDEMHandler.h"
+#include "otbOssimDEMHandler.h"
 #include "otbMacro.h"
 
 #include <cassert>
@@ -52,9 +52,9 @@
 namespace otb
 {
 /** Initialize the singleton */
-DEMHandler::Pointer DEMHandler::m_Singleton = nullptr;
+OssimDEMHandler::Pointer OssimDEMHandler::m_Singleton = nullptr;
 
-DEMHandler::Pointer DEMHandler::Instance()
+OssimDEMHandler::Pointer OssimDEMHandler::Instance()
 {
   if (m_Singleton.GetPointer() == nullptr)
   {
@@ -62,7 +62,7 @@ DEMHandler::Pointer DEMHandler::Instance()
 
     if (m_Singleton.GetPointer() == nullptr)
     {
-      m_Singleton = new DEMHandler;
+      m_Singleton = new OssimDEMHandler;
     }
     m_Singleton->UnRegister();
   }
@@ -70,7 +70,7 @@ DEMHandler::Pointer DEMHandler::Instance()
   return m_Singleton;
 }
 
-DEMHandler::DEMHandler() : m_GeoidFile(""), m_DefaultHeightAboveEllipsoid(0)
+OssimDEMHandler::OssimDEMHandler() : m_GeoidFile(""), m_DefaultHeightAboveEllipsoid(0)
 {
   assert(ossimElevManager::instance() != NULL);
 
@@ -79,7 +79,7 @@ DEMHandler::DEMHandler() : m_GeoidFile(""), m_DefaultHeightAboveEllipsoid(0)
   ossimElevManager::instance()->setUseGeoidIfNullFlag(true);
 }
 
-void DEMHandler::OpenDEMDirectory(const char* DEMDirectory)
+void OssimDEMHandler::OpenDEMDirectory(const char* DEMDirectory)
 {
   assert(ossimElevManager::instance() != NULL);
 
@@ -108,7 +108,7 @@ void DEMHandler::OpenDEMDirectory(const char* DEMDirectory)
 }
 
 
-void DEMHandler::ClearDEMs()
+void OssimDEMHandler::ClearDEMs()
 {
   assert(ossimElevManager::instance() != NULL);
 
@@ -116,12 +116,12 @@ void DEMHandler::ClearDEMs()
 }
 
 
-void DEMHandler::OpenDEMDirectory(const std::string& DEMDirectory)
+void OssimDEMHandler::OpenDEMDirectory(const std::string& DEMDirectory)
 {
   OpenDEMDirectory(DEMDirectory.c_str());
 }
 
-bool DEMHandler::IsValidDEMDirectory(const char* DEMDirectory)
+bool OssimDEMHandler::IsValidDEMDirectory(const char* DEMDirectory)
 {
   assert(ossimElevManager::instance() != NULL);
 
@@ -138,7 +138,7 @@ bool DEMHandler::IsValidDEMDirectory(const char* DEMDirectory)
   return result;
 }
 
-bool DEMHandler::OpenGeoidFile(const char* geoidFile)
+bool OssimDEMHandler::OpenGeoidFile(const char* geoidFile)
 {
   if ((ossimGeoidManager::instance()->findGeoidByShortName("geoid1996")) == nullptr)
   {
@@ -176,12 +176,12 @@ bool DEMHandler::OpenGeoidFile(const char* geoidFile)
   return false;
 }
 
-bool DEMHandler::OpenGeoidFile(const std::string& geoidFile)
+bool OssimDEMHandler::OpenGeoidFile(const std::string& geoidFile)
 {
   return OpenGeoidFile(geoidFile.c_str());
 }
 
-double DEMHandler::GetHeightAboveMSL(double lon, double lat) const
+double OssimDEMHandler::GetHeightAboveMSL(double lon, double lat) const
 {
   double   height;
   ossimGpt ossimWorldPoint;
@@ -196,12 +196,12 @@ double DEMHandler::GetHeightAboveMSL(double lon, double lat) const
   return height;
 }
 
-double DEMHandler::GetHeightAboveMSL(const PointType& geoPoint) const
+double OssimDEMHandler::GetHeightAboveMSL(const PointType& geoPoint) const
 {
   return GetHeightAboveMSL(geoPoint[0], geoPoint[1]);
 }
 
-double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat) const
+double OssimDEMHandler::GetHeightAboveEllipsoid(double lon, double lat) const
 {
   double   height;
   ossimGpt ossimWorldPoint;
@@ -216,12 +216,12 @@ double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat) const
   return height;
 }
 
-double DEMHandler::GetHeightAboveEllipsoid(const PointType& geoPoint) const
+double OssimDEMHandler::GetHeightAboveEllipsoid(const PointType& geoPoint) const
 {
   return GetHeightAboveEllipsoid(geoPoint[0], geoPoint[1]);
 }
 
-void DEMHandler::SetDefaultHeightAboveEllipsoid(double h)
+void OssimDEMHandler::SetDefaultHeightAboveEllipsoid(double h)
 {
   // Ossim does not allow retrieving the default height above
   // ellipsoid We therefore must keep it on our side
@@ -232,21 +232,21 @@ void DEMHandler::SetDefaultHeightAboveEllipsoid(double h)
   ossimElevManager::instance()->setDefaultHeightAboveEllipsoid(h);
 }
 
-double DEMHandler::GetDefaultHeightAboveEllipsoid() const
+double OssimDEMHandler::GetDefaultHeightAboveEllipsoid() const
 {
   // Ossim does not allow retrieving the default height above
   // ellipsoid We therefore must keep it on our side
   return m_DefaultHeightAboveEllipsoid;
 }
 
-unsigned int DEMHandler::GetDEMCount() const
+unsigned int OssimDEMHandler::GetDEMCount() const
 {
   assert(ossimElevManager::instance() != NULL);
 
   return ossimElevManager::instance()->getNumberOfElevationDatabases();
 }
 
-std::string DEMHandler::GetDEMDirectory(unsigned int idx) const
+std::string OssimDEMHandler::GetDEMDirectory(unsigned int idx) const
 {
   std::string demDir = "";
 
@@ -259,17 +259,17 @@ std::string DEMHandler::GetDEMDirectory(unsigned int idx) const
   return demDir;
 }
 
-std::string DEMHandler::GetGeoidFile() const
+std::string OssimDEMHandler::GetGeoidFile() const
 {
   // Ossim does not allow retrieving the geoid file path
   // We therefore must keep it on our side
   return m_GeoidFile;
 }
 
-void DEMHandler::PrintSelf(std::ostream& os, itk::Indent indent) const
+void OssimDEMHandler::PrintSelf(std::ostream& os, itk::Indent indent) const
 {
   Superclass::PrintSelf(os, indent);
-  os << indent << "DEMHandler" << std::endl;
+  os << indent << "OssimDEMHandler" << std::endl;
 }
 
 } // namespace otb
diff --git a/Modules/Adapters/OSSIMAdapters/test/CMakeLists.txt b/Modules/Adapters/OSSIMAdapters/test/CMakeLists.txt
index 6a3aed38b944b0b6e0ebe9ad73c50643e0f21ad3..6768ac1fb2a8ae5b8b4c8d357dd73c4f3ef2a7bf 100644
--- a/Modules/Adapters/OSSIMAdapters/test/CMakeLists.txt
+++ b/Modules/Adapters/OSSIMAdapters/test/CMakeLists.txt
@@ -28,7 +28,6 @@ otbOssimElevManagerTest2.cxx
 otbOssimElevManagerTest4.cxx
 otbDEMHandlerTest.cxx
 otbRPCSolverAdapterTest.cxx
-otbSarSensorModelAdapterTest.cxx
 )
 
 add_executable(otbOSSIMAdaptersTestDriver ${OTBOSSIMAdaptersTests})
@@ -466,13 +465,3 @@ otb_add_test(NAME uaTvRPCSolverAdapterValidationTest COMMAND otbOSSIMAdaptersTes
   ${INPUTDATA}/DEM/egm96.grd
   )
 
-
-otb_add_test(NAME uaTvSarSensorModelAdapter COMMAND otbOSSIMAdaptersTestDriver
-  otbSarSensorModelAdapterTest
-  ${INPUTDATA}/s1a-iw1-slc-vh-amp_xt.geom
-  )
-
-otb_add_test(NAME uaTvSarSensorModelAdapter2 COMMAND otbOSSIMAdaptersTestDriver
-  otbSarSensorModelAdapterTest
-  ${INPUTDATA}/s1a-iw1-slc-vv-20170111_Burst01_amp.geom
-  )
diff --git a/Modules/Adapters/OSSIMAdapters/test/otbDEMHandlerTest.cxx b/Modules/Adapters/OSSIMAdapters/test/otbDEMHandlerTest.cxx
index 7388ca058ccfd4ea48dd233945c4f4bc20282d2d..4d38777a01a7d85397065c2c2c872085146bd72c 100644
--- a/Modules/Adapters/OSSIMAdapters/test/otbDEMHandlerTest.cxx
+++ b/Modules/Adapters/OSSIMAdapters/test/otbDEMHandlerTest.cxx
@@ -40,8 +40,8 @@ int otbDEMHandlerTest(int argc, char* argv[])
   double      target        = atof(argv[7]);
   double      tolerance     = atof(argv[8]);
 
-  otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
-  demHandler->SetDefaultHeightAboveEllipsoid(defaultHeight);
+  auto& demHandler = otb::DEMHandler::GetInstance();
+  demHandler.SetDefaultHeightAboveEllipsoid(defaultHeight);
 
   bool fail = false;
 
@@ -50,21 +50,21 @@ int otbDEMHandlerTest(int argc, char* argv[])
 
   if (demdir != "no")
   {
-    if (!demHandler->IsValidDEMDirectory(demdir.c_str()))
+    if (!demHandler.IsValidDEMDirectory(demdir.c_str()))
     {
       std::cerr << "IsValidDEMDirectory(" << demdir << ") = false" << std::endl;
       fail = true;
     }
 
-    demHandler->OpenDEMDirectory(demdir);
-    std::cout << "GetDEMDirectory() = " << demHandler->GetDEMDirectory() << std::endl;
+    demHandler.OpenDEMDirectory(demdir);
+    std::cout << "GetDEMDirectory() = " << demHandler.GetDEMDirectory() << std::endl;
   }
 
   if (geoid != "no")
   {
     try
     {
-      demHandler->OpenGeoidFile(geoid);
+      demHandler.OpenGeoidFile(geoid);
     }
     catch (const std::exception& exception)
     {
@@ -73,11 +73,9 @@ int otbDEMHandlerTest(int argc, char* argv[])
       fail = true;
     }
 
-    std::cout << "GetGeoidFile() = " << demHandler->GetGeoidFile() << std::endl;
+    std::cout << "GetGeoidFile() = " << demHandler.GetGeoidFile() << std::endl;
   }
 
-  std::cout << "PrintSelf: " << demHandler << std::endl;
-
   otb::DEMHandler::PointType point;
   point[0] = longitude;
   point[1] = latitude;
@@ -86,13 +84,13 @@ int otbDEMHandlerTest(int argc, char* argv[])
 
   if (aboveMSL)
   {
-    height = demHandler->GetHeightAboveMSL(point);
+    height = demHandler.GetHeightAboveMSL(point);
 
     std::cout << "height above MSL (" << longitude << ", " << latitude << ") = " << height << " meters" << std::endl;
   }
   else
   {
-    height = demHandler->GetHeightAboveEllipsoid(point);
+    height = demHandler.GetHeightAboveEllipsoid(point);
     std::cout << "height above ellipsoid (" << longitude << ", " << latitude << ") = " << height << " meters" << std::endl;
   }
 
diff --git a/Modules/Adapters/OSSIMAdapters/test/otbOSSIMAdaptersTestDriver.cxx b/Modules/Adapters/OSSIMAdapters/test/otbOSSIMAdaptersTestDriver.cxx
index 6346389566b6674eaaf85106abbc5e17b8009295..b598f02738169ad9e10b2b4563b6999c9d6406cc 100644
--- a/Modules/Adapters/OSSIMAdapters/test/otbOSSIMAdaptersTestDriver.cxx
+++ b/Modules/Adapters/OSSIMAdapters/test/otbOSSIMAdaptersTestDriver.cxx
@@ -28,5 +28,4 @@ void RegisterTests()
   REGISTER_TEST(otbOssimElevManagerTest4);
   REGISTER_TEST(otbDEMHandlerTest);
   REGISTER_TEST(otbRPCSolverAdapterTest);
-  REGISTER_TEST(otbSarSensorModelAdapterTest);
 }
diff --git a/Modules/Adapters/OSSIMAdapters/test/otbRPCSolverAdapterTest.cxx b/Modules/Adapters/OSSIMAdapters/test/otbRPCSolverAdapterTest.cxx
index 10f0514383e2e6b12ec7e8d14deb06250a698864..e617e32bc2bbb0cda4a5a4faa1cd305e4f3582e2 100644
--- a/Modules/Adapters/OSSIMAdapters/test/otbRPCSolverAdapterTest.cxx
+++ b/Modules/Adapters/OSSIMAdapters/test/otbRPCSolverAdapterTest.cxx
@@ -26,6 +26,7 @@
 #include "itkEuclideanDistanceMetric.h"
 #include "otbSensorModelAdapter.h"
 #include "otbRPCSolverAdapter.h"
+#include "otbDEMHandler.h"
 
 typedef otb::Image<double>                 ImageType;
 typedef otb::ImageFileReader<ImageType>    ReaderType;
@@ -62,12 +63,12 @@ int otbRPCSolverAdapterTest(int argc, char* argv[])
   std::cout << "GeoTol: " << geoTol << " meters" << std::endl;
   std::cout << "ImgTol: " << imgTol << " pixels" << std::endl;
 
-  otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
-  demHandler->SetDefaultHeightAboveEllipsoid(0);
+  auto& demHandler = otb::DEMHandler::GetInstance();
+  demHandler.SetDefaultHeightAboveEllipsoid(0);
   if (demdir != "no")
-    demHandler->OpenDEMDirectory(demdir);
+    demHandler.OpenDEMDirectory(demdir);
   if (geoid != "no")
-    demHandler->OpenGeoidFile(geoid);
+    demHandler.OpenGeoidFile(geoid);
 
   ReaderType::Pointer reader = ReaderType::New();
   reader->SetFileName(infname);
@@ -98,7 +99,7 @@ int otbRPCSolverAdapterTest(int argc, char* argv[])
 
       currentWgs84Point = fwd2dTransform->TransformPoint(currentPoint);
 
-      double height = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(currentWgs84Point);
+      double height = demHandler.GetHeightAboveEllipsoid(currentWgs84Point);
 
       Point3DType current3DWgs84Point;
       current3DWgs84Point[0] = currentWgs84Point[0];
diff --git a/Modules/Applications/AppDescriptors/app/otbHomologousPointsExtraction.cxx b/Modules/Applications/AppDescriptors/app/otbHomologousPointsExtraction.cxx
index 653c33027248b886e5a4d85b98c41bcdf003ea29..371f7212e96624d5da0fdbb95421adf448a5dda1 100644
--- a/Modules/Applications/AppDescriptors/app/otbHomologousPointsExtraction.cxx
+++ b/Modules/Applications/AppDescriptors/app/otbHomologousPointsExtraction.cxx
@@ -150,7 +150,7 @@ private:
 
     AddChoice("mode.geobins", "Search keypoints in small spatial bins regularly spread across first image");
     SetParameterDescription("mode.geobins",
-                            "This method retrieves a set of tie points regulary spread across image 1. Corresponding bins in image 2 are retrieved using "
+                            "This method retrieves a set of tie points regularly spread across image 1. Corresponding bins in image 2 are retrieved using "
                             "sensor and geographical information if available. The first bin position takes into account the margin parameter. Bins are "
                             "cropped to the largest image region shrunk by the margin parameter for both in1 and in2 images.");
 
diff --git a/Modules/Applications/AppImageUtils/app/CMakeLists.txt b/Modules/Applications/AppImageUtils/app/CMakeLists.txt
index 8f2981b694625cefc5396bbdb254da448b500607..626dd520ec9bfb7873c6c763be5c893c02de151a 100644
--- a/Modules/Applications/AppImageUtils/app/CMakeLists.txt
+++ b/Modules/Applications/AppImageUtils/app/CMakeLists.txt
@@ -92,3 +92,13 @@ OTB_CREATE_APPLICATION(
   NAME           Mosaic
   SOURCES        otbMosaic.cxx
   LINK_LIBRARIES ${${otb-module}_LIBRARIES})
+
+OTB_CREATE_APPLICATION(
+  NAME           ResetMargin
+  SOURCES        otbResetMargin.cxx
+  LINK_LIBRARIES ${${otb-module}_LIBRARIES})
+
+OTB_CREATE_APPLICATION(
+  NAME           Synthetize
+  SOURCES        otbSynthetize.cxx
+  LINK_LIBRARIES ${${otb-module}_LIBRARIES})
diff --git a/Modules/Applications/AppImageUtils/app/otbResetMargin.cxx b/Modules/Applications/AppImageUtils/app/otbResetMargin.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..b3e7aced605f7974c6d9fff37dfe600912937737
--- /dev/null
+++ b/Modules/Applications/AppImageUtils/app/otbResetMargin.cxx
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "otbResetMarginFilter.h"
+#include "otbWrapperApplication.h"
+#include "otbWrapperApplicationFactory.h"
+
+namespace otb
+{
+namespace Wrapper
+{
+
+/**
+ * Application that fills margins to 0.
+ *
+ * This application is similar to ExtractROI with the difference the margin is
+ * kept, and filled with 0.
+ *
+ * This application is used to implement the _cut_ processing in S1Tiling
+ * chain.
+ *
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ */
+class ResetMargin : public Application
+{
+public:
+  using Self    = ResetMargin;
+  using Pointer = itk::SmartPointer<Self>;
+
+  itkNewMacro(Self);
+  itkTypeMacro(ResetMargin, otb::Wrapper::Application);
+
+private:
+  void DoInit() override
+  {
+    SetName("ResetMargin");
+
+    SetDescription("This is the ResetMargin application");
+    SetDocLongDescription(
+        "This application is similar to ExtractROI in the sense it extracts a Region of Interrest.\n"
+        "However, the region outside of the ROI isn't trimmed, but set to 0.\n"
+        "\n"
+        "The filter set lines of index < threshold.y, and of index >= threshold.y to 0\n"
+        "The filter set columns of index < threshold.x, and of index >= threshold.x to 0");
+
+    SetDocLimitations("This application only works on scalar (and complex) images.");
+    SetDocAuthors("Luc Hermitte (CS Group)");
+    SetDocSeeAlso("ManageNoData, ExtractROI");
+    AddDocTag(Tags::Manip);
+
+    AddParameter(ParameterType_InputImage,  "in",   "Input image");
+    SetParameterDescription("in", "Scalar Input image");
+
+    AddParameter(ParameterType_OutputImage, "out", "Output Image");
+    SetParameterDescription("out", "Scalar Output image");
+
+    AddParameter(ParameterType_Group, "threshold", "threshold group");
+    AddParameter(ParameterType_Group, "threshold.y", "threshold group");
+    MandatoryOff("threshold");
+    MandatoryOff("threshold.y");
+    AddParameter(ParameterType_Int, "threshold.x", "Column index threshold");
+    SetParameterDescription("threshold.x", "Column index threshold");
+    SetDefaultParameterInt("threshold.x", 0);
+
+    AddParameter(ParameterType_Int, "threshold.y.start", "Top line index threshold");
+    SetParameterDescription("threshold.y.start", "Top line index threshold");
+    SetDefaultParameterInt("threshold.y.start", 0);
+
+    AddParameter(ParameterType_Int, "threshold.y.end", "Bottom line index threshold");
+    SetParameterDescription("threshold.y.end", "Bottom line index threshold");
+    SetDefaultParameterInt("threshold.y.end", 0);
+
+    SetMinimumParameterIntValue("threshold.x",       0);
+    SetMinimumParameterIntValue("threshold.y.start", 0);
+    SetMinimumParameterIntValue("threshold.y.end",   0);
+
+    AddRAMParameter();
+
+    SetDocExampleParameterValue("in", "ResetMarginInput100x100.tiff");
+    SetDocExampleParameterValue("threshold.x",       "10");
+    SetDocExampleParameterValue("threshold.y.start", "12");
+    SetDocExampleParameterValue("threshold.y.end",    "25");
+    SetDocExampleParameterValue("out", "ResetMargin.tiff");
+    SetOfficialDocLink();
+  }
+
+  void DoUpdateParameters() override
+  {}
+
+  void DoExecute() override
+  {
+    auto const thrX = GetParameterInt("threshold.x");
+    auto const thrYtop = GetParameterInt("threshold.y.start");
+    auto const thrYbot = GetParameterInt("threshold.y.end");
+    if (thrX < 0)
+      itkExceptionMacro("The column threshold is expected to be positive");
+    if (thrYtop < 0)
+      itkExceptionMacro("The top line threshold is expected to be positive");
+    if (thrYbot < 0)
+      itkExceptionMacro("The bottom line threshold is expected to be positive");
+    if (thrX == 0 && thrYtop == 0 && thrYbot == 0)
+      itkExceptionMacro("Don't use ResetMargin to clamp nothing!");
+
+    auto filter = ResetMarginFilter<FloatImageType>::New();
+    assert(thrX >= 0);
+    assert(thrYtop >= 0);
+    assert(thrYbot >= 0);
+    filter->SetThresholdX(thrX);
+    filter->SetThresholdYtop(thrYtop);
+    filter->SetThresholdYbot(thrYbot);
+    filter->SetInput(GetParameterFloatImage("in"));
+
+    SetParameterOutputImage("out", filter->GetOutput());
+    RegisterPipeline();
+  }
+
+};
+
+} // otb::Wrapper namespace
+} // otb namespace
+
+OTB_APPLICATION_EXPORT(otb::Wrapper::ResetMargin)
diff --git a/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx b/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..027a70908b176daf768a629215c7bdc17a465655
--- /dev/null
+++ b/Modules/Applications/AppImageUtils/app/otbSynthetize.cxx
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "otbSynthetizeFilter.h"
+#include "otbWrapperApplication.h"
+#include "otbWrapperApplicationFactory.h"
+#include "otbImageFileReader.h"
+#include <set>
+
+namespace otb
+{
+namespace Wrapper
+{
+
+/**
+ * This application synthetizes/reduces multiple inputs into a single one.
+ * In that particular case, for each output pixel, this application will
+ * consider the corresponding pixels from all the input images, and keep the
+ * first one that isn't equal to 0.
+ *
+ * This application is used to implement the _concatenate_ processing in
+ * S1Tiling chain.
+ *
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \todo find a better name for the application. Alas `otbConcatenate` is
+ * already used...
+ */
+class Synthetize : public Application
+{
+public:
+  using Self    = Synthetize;
+  using Pointer = itk::SmartPointer<Self>;
+
+  itkNewMacro(Self);
+  itkTypeMacro(Synthetize, otb::Wrapper::Application);
+
+private:
+  using ReaderType = otb::ImageFileReader<FloatImageType>;
+
+  void DoInit() override
+  {
+    SetName("Synthetize");
+
+    SetDescription("This is the Synthetize application");
+    SetDocLongDescription("Concatenate a list of images of the same size into a single single-channel image.\n\
+        It keeps the first non-null pixel value found in the input list.");
+
+    SetDocLimitations("This application will break incoming pipelines.");
+    SetDocAuthors("Luc Hermitte (CS Group)");
+    SetDocSeeAlso("");
+    AddDocTag(Tags::Manip);
+
+    AddParameter(ParameterType_StringList,  "il",  "Input images list");
+    SetParameterDescription("il", "Input image list");
+
+    AddParameter(ParameterType_OutputImage, "out", "Output Image");
+    SetParameterDescription("out","Output image.");
+
+    AddRAMParameter();
+
+    SetDocExampleParameterValue("il", "s1a_33NWB_vv_DES_007_20200108t044150.tif s1a_33NWB_vv_DES_007_20200108t044215.tif");
+    SetDocExampleParameterValue("out", "s1a_33NWB_vv_DES_007_20200108txxxxxx.tif");
+    SetOfficialDocLink();
+  }
+
+  void DoUpdateParameters() override
+  {}
+
+  void DoExecute() override
+  {
+    // Get the input image list
+    auto inNameList = GetParameterStringList("il");
+
+    // checking the input images list validity
+    auto const nbImages = inNameList.size();
+
+    if (nbImages == 0)
+    {
+      itkExceptionMacro("No input Image set...; please set at least one input image");
+    }
+
+    auto functor = [](auto input) {
+      assert(!input.empty());
+      auto const wh = std::find_if(
+          input.begin(), input.end()-1,
+          [](auto v){ return v != 0;});
+      return *wh;
+    };
+    auto filter = MakeSynthetizeFilter<FloatImageType, FloatImageType>(functor);
+
+    for (unsigned int i = 0; i < nbImages; i++)
+    {
+      // Given the explicit use of a Reader, this application cannot be used in
+      // a in-memory pipeline
+      auto reader = ReaderType::New();
+      // currentImage->SetExtendedFileName(inNameList[i]);
+      reader->SetFileName(inNameList[i]);
+      auto currentImage = reader->GetOutput();
+      currentImage->UpdateOutputInformation();
+
+      otbAppLogINFO(<< "Image #" << i + 1 << " has " << currentImage->GetNumberOfComponentsPerPixel() << " components");
+
+      filter->SetInput(i, currentImage);
+      m_Cache.insert(reader);
+    }
+
+    SetParameterOutputImage("out", filter->GetOutput());
+    RegisterPipeline(); // TODO: check!!
+  }
+
+  // Needed to register the inputs handled manually
+  // and not with a VectorImageList through GetParameterImageList
+  std::set<ReaderType::Pointer> m_Cache;
+};
+
+} // otb::Wrapper namespace
+} // otb namespace
+
+OTB_APPLICATION_EXPORT(otb::Wrapper::Synthetize)
diff --git a/Modules/Applications/AppImageUtils/test/CMakeLists.txt b/Modules/Applications/AppImageUtils/test/CMakeLists.txt
index 350b31b3df4c05aaa499ac0625e17ab3972729e0..6ad99ac2c011201cae2f6084b744f41ab64e4af7 100644
--- a/Modules/Applications/AppImageUtils/test/CMakeLists.txt
+++ b/Modules/Applications/AppImageUtils/test/CMakeLists.txt
@@ -120,6 +120,18 @@ otb_test_application(NAME apTvUtExtractROIExtentFitVect
                              ${OTBAPP_BASELINE}/apTvUtExtractROI.tif
                              ${TEMP}/apTvUtExtractROIExtentFitVect.tif)
 
+#----------- Clamp ROI tests  ----------------
+otb_test_application(NAME apTvUtResetMargin
+                     APP  ResetMargin
+                     OPTIONS -in ${INPUTDATA}/ResetMarginInput100x100.tiff
+                             -out ${TEMP}/apTvUtResetMargin.tif
+                             -threshold.x       10
+                             -threshold.y.start 12
+                             -threshold.y.end   25
+                     VALID   --compare-image ${NOTOL}
+                             ${OTBAPP_BASELINE}/ResetMarginBaseline100x100.tiff
+                             ${TEMP}/apTvUtResetMargin.tif)
+
 #----------- Rescale TESTS ----------------
 otb_test_application(NAME  apTvUtRescaleTest
                      APP  Rescale
@@ -203,6 +215,11 @@ otb_test_application(NAME apTvUtQuicklookROI1Channel
                              ${TEMP}/apTvUtQuicklookROI1Channel.tif
                      )
 
+# this tests has two baseline to take into account the bug described in
+# https://github.com/opengeospatial/ogc_api_coverages/issues/92
+# (half pixel shift applied in the wrong direction to GCPs with the PixelIsPoint convention)
+# The first baseline corresponds to the gdal < 3.1.3 case, 
+# and the second baseline correspond to gdal >= 3.1.3 case.
 otb_test_application(NAME apTvUtQuicklookWithGCP
                      APP Quicklook
                      OPTIONS -in ${INPUTDATA}/spot5SubWithGcps.tif
@@ -252,6 +269,16 @@ otb_test_application(NAME apTvUtConcatenateImages_1Image
                              ${TEMP}/apTvUtConcatenateImages_1Image.tif)
 
 
+#----------- Synthetize TESTS ----------------
+otb_test_application(NAME apTvUtSynthetize
+                     APP  Synthetize
+                     OPTIONS -il ${INPUTDATA}/s1a_33NWB_vv_DES_007_20200108t044150_100x100.tif
+                                 ${INPUTDATA}/s1a_33NWB_vv_DES_007_20200108t044215_100x100.tif
+                             -out ${TEMP}/apTvUtSynthetize.tif
+                     VALID   --compare-image ${NOTOL}
+                             ${OTBAPP_BASELINE}/s1a_33NWB_vv_DES_007_20200108txxxxxx_100x100.tif
+                             ${TEMP}/apTvUtSynthetize.tif)
+
 #----------- MultiResolutionPyramid TESTS ----------------
 
 #----------- PixelValue TESTS ----------------
@@ -443,8 +470,8 @@ otb_test_application(NAME apTvUtSplitImage
 #----------- Mosaic TESTS ----------------
 otb_test_application(NAME MosaicTestLargeFeathering
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestLargeFeathering.tif uint8 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestLargeFeathering.tif uint8
                              -comp.feather large
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestLargeFeathering.tif
@@ -453,9 +480,9 @@ otb_test_application(NAME MosaicTestLargeFeathering
 
 otb_test_application(NAME MosaicTestSlimFeathering
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestSlimFeathering.tif uint8 
-                             -comp.feather slim 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestSlimFeathering.tif uint8
+                             -comp.feather slim
                              -comp.feather.slim.length 100
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestSlimFeathering.tif
@@ -464,9 +491,9 @@ otb_test_application(NAME MosaicTestSlimFeathering
 
 otb_test_application(NAME MosaicTestSimpleWithHarmoBandRmse
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestSimpleWithHarmoBandRmse.tif uint8 
-                             -harmo.method band 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestSimpleWithHarmoBandRmse.tif uint8
+                             -harmo.method band
                              -harmo.cost rmse
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestSimpleWithHarmoBandRmse.tif
@@ -474,9 +501,9 @@ otb_test_application(NAME MosaicTestSimpleWithHarmoBandRmse
 
 otb_test_application(NAME MosaicTestSimpleWithHarmoRgbRmse
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestSimpleWithHarmoRgbRmse.tif uint8 
-                             -harmo.method rgb 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestSimpleWithHarmoRgbRmse.tif uint8
+                             -harmo.method rgb
                              -harmo.cost rmse
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestSimpleWithHarmoRgbRmse.tif
@@ -484,18 +511,18 @@ otb_test_application(NAME MosaicTestSimpleWithHarmoRgbRmse
 
 otb_test_application(NAME MosaicTestSimpleWithCutline
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestSimpleWithCutline.tif uint8 
-                             -vdcut ${INPUTDATA}/SP67_FR_subset_1_cutline.shp ${INPUTDATA}/SP67_FR_subset_2_cutline.shp 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestSimpleWithCutline.tif uint8
+                             -vdcut ${INPUTDATA}/SP67_FR_subset_1_cutline.shp ${INPUTDATA}/SP67_FR_subset_2_cutline.shp
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestSimpleWithCutline.tif
                              ${TEMP}/apTvMosaicTestSimpleWithCutline.tif)
 
 otb_test_application(NAME MosaicTestSimpleWithVdstats
                      APP  Mosaic
-                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif 
-                             -out ${TEMP}/apTvMosaicTestSimpleWithVdstats.tif uint8 
-                             -vdstats ${INPUTDATA}/SP67_FR_subset_1_cutline.shp ${INPUTDATA}/SP67_FR_subset_2_cutline.shp 
+                     OPTIONS -il ${INPUTDATA}/SP67_FR_subset_1.tif ${INPUTDATA}/SP67_FR_subset_2.tif
+                             -out ${TEMP}/apTvMosaicTestSimpleWithVdstats.tif uint8
+                             -vdstats ${INPUTDATA}/SP67_FR_subset_1_cutline.shp ${INPUTDATA}/SP67_FR_subset_2_cutline.shp
                      VALID   --compare-image ${EPSILON_8}
                              ${BASELINE}/apTvMosaicTestSimpleWithVdstats.tif
                              ${TEMP}/apTvMosaicTestSimpleWithVdstats.tif)
diff --git a/Modules/Applications/AppProjection/app/otbGenerateRPCSensorModel.cxx b/Modules/Applications/AppProjection/app/otbGenerateRPCSensorModel.cxx
index ae0432c400e2f195840c0ea3937b9960e4ef91b9..d8dfa50b3c8605d55428ab2fdc6888ab8cf805b5 100644
--- a/Modules/Applications/AppProjection/app/otbGenerateRPCSensorModel.cxx
+++ b/Modules/Applications/AppProjection/app/otbGenerateRPCSensorModel.cxx
@@ -29,6 +29,7 @@
 #include "otbGenericRSTransform.h"
 #include "otbOGRDataSourceWrapper.h"
 #include "ogrsf_frmts.h"
+#include "otbDEMHandler.h"
 
 namespace otb
 {
@@ -142,7 +143,7 @@ private:
       {
         std::istringstream iss(line);
         iss >> x >> y >> lon >> lat;
-        z = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(lon, lat);
+        z = otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(lon, lat);
 
         otbAppLogDEBUG("Adding tie point x=" << x << ", y=" << y << ", z=" << z << ", lon=" << lon << ", lat=" << lat);
 
diff --git a/Modules/Applications/AppProjection/app/otbOrthoRectification.cxx b/Modules/Applications/AppProjection/app/otbOrthoRectification.cxx
index ae1916ca7e991fa5970392bd8d79e89792e1383a..244e43fea80185fc146ac472301233dd5bf4cab6 100644
--- a/Modules/Applications/AppProjection/app/otbOrthoRectification.cxx
+++ b/Modules/Applications/AppProjection/app/otbOrthoRectification.cxx
@@ -255,7 +255,7 @@ private:
     {
 
       // Clear and reset the DEM Handler
-      otb::DEMHandler::Instance()->ClearDEMs();
+      otb::DEMHandler::GetInstance().ClearDEMs();
       otb::Wrapper::ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(this, "elev");
 
       // input image
diff --git a/Modules/Applications/AppProjection/app/otbRefineSensorModel.cxx b/Modules/Applications/AppProjection/app/otbRefineSensorModel.cxx
index 4796181cba08ef9e5bb1fd86d7f796271173eb50..9dcedb3e2e42b67a14ca554eede60908a11d2226 100644
--- a/Modules/Applications/AppProjection/app/otbRefineSensorModel.cxx
+++ b/Modules/Applications/AppProjection/app/otbRefineSensorModel.cxx
@@ -29,6 +29,7 @@
 #include "otbGenericRSTransform.h"
 #include "otbOGRDataSourceWrapper.h"
 #include "ogrsf_frmts.h"
+#include "otbDEMHandler.h"
 
 namespace otb
 {
@@ -164,7 +165,7 @@ private:
         nextpos                        = line.find_first_of("\t", pos);
         lat                            = atof(line.substr(pos, nextpos).c_str());
 
-        z = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(lon, lat);
+        z = otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(lon, lat);
 
         otbAppLogDEBUG("Adding tie point x=" << x << ", y=" << y << ", z=" << z << ", lon=" << lon << ", lat=" << lat);
 
diff --git a/Modules/Applications/AppStereo/app/otbStereoRectificationGridGenerator.cxx b/Modules/Applications/AppStereo/app/otbStereoRectificationGridGenerator.cxx
index a2e2d3f33eb4f9bc30d96efef296d277bad39a7c..6a995450dff580101d7c13864a93eb6865d5aa3f 100644
--- a/Modules/Applications/AppStereo/app/otbStereoRectificationGridGenerator.cxx
+++ b/Modules/Applications/AppStereo/app/otbStereoRectificationGridGenerator.cxx
@@ -311,7 +311,7 @@ private:
       AddProcess(m_StatisticsFilter->GetStreamer(), "Computing DEM statistics ...");
       m_StatisticsFilter->Update();
 
-      otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(m_StatisticsFilter->GetMean());
+      otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(m_StatisticsFilter->GetMean());
 
       EnableParameter("epi.elevation.avgdem.value");
       SetParameterFloat("epi.elevation.avgdem.value", m_StatisticsFilter->GetMean());
diff --git a/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx b/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx
index 0c52199204a5beb9b8797026329c0d370c2f3a0e..692a02b7ca66dc5f1918bca7fd6995c17d4657c8 100644
--- a/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx
+++ b/Modules/Applications/AppTextures/app/otbPantexTextureExtraction.cxx
@@ -69,7 +69,7 @@ private:
     SetDocLimitations("None");
 
     SetDocAuthors("OTB-Team");
-    SetDocSeeAlso(" Pesari, M., A. Gerhardinger, F. Kayitakire. 2008.  A robust built-up area precense"
+    SetDocSeeAlso(" Pesari, M., A. Gerhardinger, F. Kayitakire. 2008.  A robust built-up area presence"
       " index by anisotropic rotation-invariant textural measure."
       " IEEE Journal of selected topics in applied earth observations and remote sensing.Vol1, NO3.");
 
@@ -89,7 +89,7 @@ private:
 
     AddParameter(ParameterType_Float, "min", "Image minimum");
     SetParameterDescription("min", "Input image minimum. If this parameter is not set, the application will compute "
-                            "the minumum of the image.");
+                            "the minimum of the image.");
     MandatoryOff("min");
 
     AddParameter(ParameterType_Float, "max", "Image maximum");
diff --git a/Modules/Core/Common/include/otbInterval.h b/Modules/Core/Common/include/otbInterval.h
new file mode 100644
index 0000000000000000000000000000000000000000..f37c2c04343ec95cdf73b81079ddebcc981730dc
--- /dev/null
+++ b/Modules/Core/Common/include/otbInterval.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbInterval_h
+#define otbInterval_h
+
+#include "itkIntTypes.h"
+#include <algorithm>
+#include <cassert>
+#include <ostream>
+
+namespace otb
+{
+/** Simplified index interval.
+ * Inspired by `boost::numeric::interval`.
+ *
+ * \invariant Can be empty.
+ * \invariant lower <= upper
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+class Interval
+{
+public:
+  using IndexType = itk::IndexValueType;
+  using SizeType  = itk::SizeValueType;
+
+  /** Init constructor from the pair of its extremities. */
+  constexpr Interval(IndexType l, IndexType u) noexcept
+    : m_lower(l), m_upper(u)
+    {
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+      assert(l <= u);
+#endif
+    }
+
+  /** Alternate factory function from a position and a length. */
+  static constexpr Interval OfLength(IndexType low, SizeType len) noexcept{
+    return Interval{low, IndexType(low+len)};
+  }
+
+  constexpr SizeType size() const noexcept
+  { return m_upper - m_lower; }
+
+  constexpr bool empty() const noexcept
+  { return m_lower == m_upper; }
+
+  constexpr IndexType lower() const noexcept
+  { return m_lower; }
+
+  constexpr IndexType upper() const noexcept
+  { return m_upper; }
+
+  /** Computes the intersection between two interals.
+   * @return their intersection
+   * @return {0,0} if theyr don't intersect.
+   *
+   * @note this function is an hidden friend
+   */
+  friend constexpr Interval intersect(
+      Interval const& lhs, Interval const& rhs) noexcept
+  {
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+    auto const low = std::max(lhs.lower(), rhs.lower());
+    auto const upp = std::min(lhs.upper(), rhs.upper());
+
+    return low <= upp ? Interval{low, upp} : Interval{0,0};
+#else
+    // MSVC version supported is not C++14 compliant
+    return std::max(lhs.lower(), rhs.lower()) <= std::min(lhs.upper(), rhs.upper())
+      ? Interval{std::max(lhs.lower(), rhs.lower()), std::min(lhs.upper(), rhs.upper())}
+      : Interval{0,0};
+#endif
+  }
+
+  /** Stream inserter for intervals.
+   * @note this function is an hidden friend
+   */
+  friend std::ostream & operator<<(std::ostream & os, Interval const& v)
+  {
+    return os << '[' << v.lower() << ".." << v.upper() << '[';
+  }
+
+private:
+  IndexType m_lower;
+  IndexType m_upper;
+};
+} // otb namespace
+
+#endif  // otbInterval_h
diff --git a/Modules/Core/Common/include/otbLogHelpers.h b/Modules/Core/Common/include/otbLogHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..f205a69355f5a61e6bee762ff66f41dc71eaaaa2
--- /dev/null
+++ b/Modules/Core/Common/include/otbLogHelpers.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbLogHelpers_h
+#define otbLogHelpers_h
+
+#include "itkImageRegion.h"
+#include <ostream>
+
+namespace otb
+{
+
+/** Helper class to log region in a more human readable way:
+ * e.g.
+ * \code
+ x ∈ [0..42[, y ∈ [12..24[, size=42x12 @(0, 12)
+ * \endcode
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+struct NeatRegionLogger
+{
+  using RegionType = itk::ImageRegion<2u>;
+  NeatRegionLogger(RegionType const& region) : m_region(region) {}
+
+  friend std::ostream & operator<<(std::ostream & os, NeatRegionLogger const& r)
+  {
+    auto const& size = r.m_region.GetSize();
+    auto const& idx  = r.m_region.GetIndex();
+    auto const  idx_x1  = idx[0];
+    int  const  idx_x2  = idx[0] + size[0];
+    int  const  idx_y1  = idx[1];
+    auto const  idx_y2  = idx[1] + size[1];
+    os
+      << "x ∈ ["<<idx_x1 << ".." << idx_x2 << '['
+      << ", y ∈ ["<<idx_y1 << ".." << idx_y2 << '['
+      << ", size=" << size[0]<<'x'<<size[1] << " @("<<idx_x1<<", "<<idx_y1<<')'
+      ;
+    return os ;
+  }
+  RegionType const& m_region;
+};
+} // otb namespace
+
+#endif  // otbLogHelpers_h
diff --git a/Modules/Core/Common/include/otbSpan.h b/Modules/Core/Common/include/otbSpan.h
new file mode 100644
index 0000000000000000000000000000000000000000..93d27f4617b16dfd6c502625f7e8b1b36668b090
--- /dev/null
+++ b/Modules/Core/Common/include/otbSpan.h
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef Span_h
+#define Span_h
+
+#include <type_traits>
+#include <iterator>
+#include <limits>
+#include <cassert>
+
+namespace otb
+{
+#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
+  // In C++ (only; fixed in C++14), constexpr implies const on member
+  // functions, and OTB support VC++14 which is not C++14 compliant on this
+  // topic.
+  // Hence the workaround...
+  // TODO: get rid with this hack along VC++14
+#  define OTB_MB_CSTXPR constexpr
+#else
+#  define OTB_MB_CSTXPR
+#endif
+
+/** Span class inspired by C++20 standard.
+ *
+ * \invariant `size() == 0 or data() != nullptr`
+ *
+ * \note Unlike C++20 `std::span` this implementation doesn't follow Lakos
+ * Rule but instead non-throwing functions are `noexcept` as suggested in
+ * https://wg21.link/p1656. Beware to not expect `operator[]` to always be
+ * `noexcept` as it won't be anymore once this class is deprecated in favour
+ * of `std::span` in a few years.
+ *
+ * \note This implementation only support spans with dynamic extents. Static
+ * extents are not supported (yet?)
+ *
+ * \todo fix RW / RO interface
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename T> struct Span
+{
+  /**\name Typedefs */
+  //@{
+  using element_type           = T;
+  using value_type             = std::remove_cv_t<T>;
+  using index_type             = std::size_t;
+  using difference_type        = std::ptrdiff_t;
+  using pointer                = T*;
+  using const_pointer          = T const*;
+  using reference              = T&;
+  using const_reference        = T const&;
+  using iterator               = T*;
+  using const_iterator         = T const*;
+  using reverse_iterator       = std::reverse_iterator<iterator>;
+  using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+  //@}
+
+  /**\name Constructors */
+  //@{
+  constexpr Span() noexcept = default;
+  constexpr Span(pointer ptr, index_type count) noexcept
+    : m_buffer(ptr), m_size(count)
+    {
+      assert(! (!ptr) xor (!count));
+    }
+  constexpr Span(pointer first, pointer last) noexcept
+    : Span(first, last - first)
+    {
+      assert(! (!first) xor (!last));
+      assert(first <= last);
+    }
+  template <std::size_t N> constexpr Span(element_type (&arr)[N]) noexcept
+    : Span(arr, N)
+    {}
+
+  /** Converting constructor from a contiguous container.
+   * \pre The Container shall be contiguous
+   * \warning The lifetime of the span shall not exceed the one of the container.
+   * Be sure to not store the span locally, and initialize it from a rvalue.
+   * The use case where a span is initialized from a rvalue shall be restricted
+   * to function parameters.
+   * \code
+   * std::vector<T> f();
+   * void g(Span<T> sp);
+   * ...
+   * Span<T> sp(f()); // NO!!! Use after release
+   * g(f());          // OK!!
+   * \endcode
+   *
+   * \todo static_assert the container is contiguous
+   */
+  template <class Container> constexpr Span(Container&& cont) noexcept
+    : Span(cont.data(), cont.size())
+    {
+      // We cannot use op[] which has an assertion sometimes.
+      // assert(&const[size()] == (&cont[0] + size()));
+      // Beside, it's not noexcept.
+    }
+  template <class U> constexpr Span(const otb::Span<U>& s) noexcept
+    : Span(s.data(), s.size())
+    {}
+  constexpr Span(const Span& other) noexcept = default;
+  //@}
+
+  /// shallow assignment
+  Span& operator=(Span const&) noexcept = default;
+
+  /// No-op destructor
+  ~Span() = default;
+
+  /**\name Iterators */
+  //@{
+  OTB_MB_CSTXPR iterator   begin ()       noexcept { return data(); }
+  OTB_MB_CSTXPR iterator   end   ()       noexcept { return data()+size(); }
+  constexpr const_iterator begin () const noexcept { return data(); }
+  constexpr const_iterator end   () const noexcept { return data()+size(); }
+  constexpr const_iterator cbegin() const noexcept { return data(); }
+  constexpr const_iterator cend  () const noexcept { return data()+size(); }
+
+  OTB_MB_CSTXPR reverse_iterator   rbegin ()       noexcept { return reverse_iterator(end()); }
+  OTB_MB_CSTXPR reverse_iterator   rend   ()       noexcept { return reverse_iterator(begin()); }
+  constexpr const_reverse_iterator crbegin() const noexcept { return reverse_const_iterator(cend()); }
+  constexpr const_reverse_iterator crend  () const noexcept { return reverse_const_iterator(cbegin()); }
+  //@}
+
+  /**\name Element access */
+  //@{
+  OTB_MB_CSTXPR pointer     data ()       noexcept { return m_buffer; }
+  constexpr const_pointer   data () const noexcept { return m_buffer; }
+  OTB_MB_CSTXPR reference   front()       noexcept { assert(!empty()); return *data(); }
+  constexpr const_reference front() const noexcept { assert(!empty()); return *data(); }
+  OTB_MB_CSTXPR reference   back ()       noexcept { assert(!empty()); return *data()+size()-1; }
+  constexpr const_reference back () const noexcept { assert(!empty()); return *data()+size()-1; }
+
+  OTB_MB_CSTXPR reference   operator[](index_type p)       noexcept
+  {
+    assert(p < size());
+    return data()[p];
+  }
+  constexpr const_reference operator[](index_type p) const noexcept
+  {
+    assert(p < size());
+    return data()[p];
+  }
+  //@}
+
+  /**\name Observers */
+  //@{
+  constexpr index_type size () const noexcept { return m_size; }
+  constexpr bool       empty() const noexcept { return size() == 0; }
+  //@}
+
+  /**\name Subviews */
+  //@{
+  constexpr Span first(index_type n) const noexcept
+  { assert(n < size()); return Span(data(), n);}
+  constexpr Span last(index_type n) const noexcept
+  { assert(n < size()); return Span(data()-n, n);}
+
+  constexpr Span subspan(index_type offset, index_type count = std::numeric_limits<index_type>::max()) const noexcept
+  {
+    assert(offset <= size());
+    if (count == std::numeric_limits<index_type>::max())
+    {
+      count = size() - offset;
+    }
+
+    assert(count <= (size() - offset));
+    return Span(data()+offset, count);
+
+  }
+  //@}
+
+private:
+  pointer    m_buffer = nullptr;
+  index_type m_size   = 0;
+};
+
+/**
+ * Helper function to make a span from a range defined with pointers.
+ * Compensate the fact we are not have access to C++17 `span{ptr1, ptr2}`.
+ * \tparam T  Auto deduced type of the elements
+ * \param[in] first  start of the memory zone
+ * \param[in] last   end of the memory zone
+ * \return a span over `[first, last)`
+ * \throw None
+ * \pre `NOT first != nullptr XOR last != nullptr`
+ * \pre `first < last`
+ * \pre `[first, last)` can be iterated
+ * \see `otb::Span<>`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename T>
+inline
+auto make_span(T* first, T* last) noexcept
+{
+  return Span<T>(first, last);
+}
+
+/**
+ * Helper function to make a span from a range defined with a pointer plus a
+ * size.
+ * Compensate the fact we are not have access to C++17 `span{ptr, count}`.
+ * \tparam T  Auto deduced type of the elements
+ * \param[in] first  start of the memory zone
+ * \param[in] count  number of elements in the span.
+ * \return a span over `[first, first+count)`
+ * \throw None
+ * \pre `NOT first != nullptr XOR count != 0`
+ * \see `otb::Span<>`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename T>
+inline
+auto make_span(T* first, std::size_t count) noexcept
+{
+  return Span<T>(first, count);
+}
+
+/**
+ * Helper function to make a span from a static array.
+ * Compensate the fact we are not have access to C++17 `span{array}`.
+ * \tparam T  Auto deduced type of the elements
+ * \tparam N  Auto deduced number of elements in the array
+ * \param[in] array  static array
+ * \return a span over `[&array[0], &array[N])`
+ * \throw None
+ * \see `otb::Span<>`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename T, std::size_t N>
+inline
+auto make_span(T (&arr)[N]) noexcept
+{
+  return Span<T>(arr);
+}
+
+/**
+ * Helper function to make a span from a contiguous container.
+ * Compensate the fact we are not have access to C++17 `span{container}`.
+ * \tparam ContiguousContainer  Auto deduced type of the container
+ * \param[in] cont  container of contiguous elements
+ * \return a span over `[c.data(), c.size())`
+ * \throw None
+ * \see `otb::Span<>`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename ContiguousContainer>
+inline
+auto make_span(ContiguousContainer & c) noexcept
+{
+  return Span<decltype(*c.data())>(c);
+}
+
+}
+// otb namespace
+
+
+#endif // Span_h
diff --git a/Modules/Core/Common/include/otbZipIterator.h b/Modules/Core/Common/include/otbZipIterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4bce7b6a9c8425a79ff224e74487d953475ccba
--- /dev/null
+++ b/Modules/Core/Common/include/otbZipIterator.h
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbZipIterator_h
+#define otbZipIterator_h
+
+#include "otbSpan.h"
+#include "itkMacro.h"
+#include <type_traits>
+#include <vector>
+#include <cassert>
+
+namespace otb
+{
+namespace internals
+{
+
+struct ConstTag {};
+struct MutableTag {};
+
+/**
+ * Wrapper to present list of iterators as a single iterator.
+ *
+ * \invariant This class shall not be inherited.
+ * \invariant All sub iterators are always synchronised (same region, size...)
+ * \invariant `!m_iterators.empty()`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ */
+template <typename TImageIterator, typename ConstOrMutable>
+class ZipIterator
+{
+public:
+  /**\name ITK Constants and Typedefs */
+  //@{
+  /** Iterator type for zipped iterators. */
+  using ImageIteratorType     = TImageIterator;
+
+  /** Image type alias support */
+  using ImageType             = typename TImageIterator::ImageType;
+
+  /** Dimension of the image the iterator walks.  This constant is needed so
+   * functions that are templated over image iterator type (as opposed to
+   * being templated over pixel type and dimension) can have compile time
+   * access to the dimension of the image that the iterator walks. */
+  static constexpr unsigned int ImageIteratorDimension = ImageIteratorType::ImageIteratorDimension;
+
+  using Self                  = ZipIterator;
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacroNoParent(ZipIterator);
+
+  /** Index type alias support */
+  using IndexType             = typename ImageIteratorType::IndexType;
+
+  /** Size type alias support */
+  using SizeType              = typename ImageIteratorType::SizeType;
+
+  /** Offset type alias support */
+  using OffsetType            = typename ImageIteratorType::OffsetType;
+
+  /** Region type alias support */
+  using RegionType            = typename ImageIteratorType::RegionType;
+
+  /** PixelContainer type alias support. Used to refer to the container for
+   * the pixel data. While this was already typdef'ed in the superclass
+   * it needs to be redone here for this subclass to compile properly with gcc. */
+  using PixelContainer        = typename ImageIteratorType::PixelContainer;
+  using PixelContainerPointer = typename PixelContainer::Pointer;
+
+  /** Internal Pixel Type */
+  using InternalPixelType     = typename ImageIteratorType::InternalPixelType;
+
+  /** External Pixel Type */
+  using PixelType             = typename ImageIteratorType::PixelType;
+
+  /**  Accessor type that convert data between internal and external
+   *  representations. */
+  using AccessorType          = typename ImageIteratorType::AccessorType;
+  using AccessorFunctorType   = typename ImageIteratorType::AccessorFunctorType;
+  //@}
+
+  /**\name Constructions & Destruction
+   *
+   * This class follows the rule of 0/5
+   */
+  //@{
+  ZipIterator           ()                   = default;
+  ~ZipIterator          ()                   = default;
+  ZipIterator           (ZipIterator const&) = default;
+  ZipIterator           (ZipIterator     &&) = default;
+  ZipIterator& operator=(ZipIterator const&) = default;
+  ZipIterator& operator=(ZipIterator     &&) = default;
+
+  /** Conversion constructor.
+   * Converts from mutable to const iterator
+   * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/
+   */
+  template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>>
+  ZipIterator(ZipIterator<ImageIteratorType, MutableTag> const& rhs)
+    : m_iterators(rhs.m_iterators())
+    {}
+
+  /** Conversion move constructor.
+   * Move converts from mutable to const iterator
+   * \see https://quuxplusone.github.io/blog/2018/12/01/const-iterator-antipatterns/
+   */
+  template <bool IsConst_ = std::is_same<ConstOrMutable, ConstTag>::value, class = std::enable_if<IsConst_>>
+  ZipIterator(ZipIterator<ImageIteratorType, MutableTag> && rhs)
+    : m_iterators(move(rhs.m_iterators()))
+    {}
+
+  /**
+   * Init Constructor.
+   * Constructs a `ZipIterator` from a list of Images and a Region.
+   * \param[in,out] images  List of images
+   * \param[in]     region  Region to iterate over
+   * \pre There should be at least one image
+   */
+  ZipIterator(Span<ImageType * const> images, RegionType const& region)
+  {
+    assert(! images.empty());
+    m_iterators.reserve(images.size());
+
+    for (auto & im: images)
+      m_iterators.emplace_back(im, region);
+  }
+
+  // static_assert(std::is_copy_constructible<Self>::value, "Requires copy construction");
+  // static_assert(std::is_trivially_copy_constructible<Self>::value, "Requires trivial copy construction");
+  //@}
+
+  /**\name Comparison */
+  //@{
+  friend bool operator==(ZipIterator const& lhs, ZipIterator const& rhs)
+  {
+    assert(!lhs.m_iterators.empty());
+    assert(lhs.m_iterators.size() == rhs.m_iterators.size());
+
+    return lhs.m_iterators.front() == rhs.m_iterators.front();
+  }
+  friend bool operator!=(ZipIterator const& lhs, ZipIterator const& rhs)
+  { return ! (lhs == rhs); }
+
+  friend bool operator<=(ZipIterator const& lhs, ZipIterator const& rhs)
+  {
+    assert(!lhs.m_iterators.empty());
+    assert(lhs.m_iterators.size() == rhs.m_iterators.size());
+
+    return lhs.m_iterators.front() <= rhs.m_iterators.front();
+  }
+  friend bool operator<(ZipIterator const& lhs, ZipIterator const& rhs)
+  {
+    assert(!lhs.m_iterators.empty());
+    assert(lhs.m_iterators.size() == rhs.m_iterators.size());
+
+    return lhs.m_iterators.front() < rhs.m_iterators.front();
+  }
+  friend bool operator>=(ZipIterator const& lhs, ZipIterator const& rhs)
+  { return ! (lhs < rhs); }
+  friend bool operator>(ZipIterator const& lhs, ZipIterator const& rhs)
+  { return ! (lhs <= rhs); }
+  //@}
+
+  /**\name ITK iterator interface */
+  //@{
+  // What about GetIndex() and SetIndex ?
+  /** Fetch the region iterated by the iterator. */
+  auto const& GetRegion() const
+  {
+    assert(!m_iterators.empty());
+    return m_iterators.front().GetRegion();
+  }
+
+  /** Set the region iterated by the iterator. */
+  void SetRegion(RegionType const& region)
+  {
+    for (auto & it : m_iterators)
+      it.SetRegion(region);
+  }
+
+  /** Moves the iterator to the begin of the region iterated. */
+  Self& GoToBegin() {
+    for (auto & it : m_iterators)
+      it.GoToBegin();
+    return *this;
+  }
+  /** Moves the iterator to the end of the region iterated. */
+  Self& GoToEnd() {
+    for (auto & it : m_iterators)
+      it.GoToEnd();
+    return *this;
+  }
+
+  /** Tells whether the iterator is at the begin of the region iterated. */
+  bool IsAtBegin() const {
+    assert(!m_iterators.empty());
+    return m_iterators.front().IsAtBegin();
+  }
+
+  /** Tells the iterator is at the end of the region iterated. */
+  bool IsAtEnd() const {
+    assert(!m_iterators.empty());
+    return m_iterators.front().IsAtEnd();
+  }
+
+  /** Pre-increment the iterator.
+   * As post-increment is less efficient, it hasn't been provided.
+   */
+  Self& operator++() {
+    assert(!IsAtEnd());
+    for (auto & it : m_iterators)
+      ++it;
+    return *this;
+  }
+  /** Removed post-increment operator.
+   * Please use the  preincrement operator!
+   */
+  Self operator++(int) = delete;
+
+  /** Internal typedef to the type holding the list of ITK iterators. */
+  using ImageIteratorList_t = std::vector<ImageIteratorType>;
+  /** Internal Pixel Proxy Type returned by `ZipIterator`.
+   * \author Luc Hermitte (CS Group)
+   * \copyright CNES
+   * \ingroup OTBCommon
+   */
+  struct PixelListProxy
+  {
+    /** Init constructor.
+     * @param[in] iterators List of ITK iterators
+     */
+    explicit PixelListProxy(ImageIteratorList_t const& iterators) noexcept
+      : m_iterators(iterators)
+      {}
+    bool empty()   const noexcept {return m_iterators.empty();}
+    auto size()    const noexcept {return m_iterators.size();}
+
+    /** Internal C++ iterator over the components of the Pixel Proxy.
+     * \author Luc Hermitte (CS Group)
+     * \copyright CNES
+     * \ingroup OTBCommon
+     */
+    struct iterator__
+    {
+      using difference_type   = typename ImageIteratorList_t::difference_type;
+      using value_type        = decltype(typename ImageIteratorList_t::const_iterator{}->Get());
+      using pointer           = value_type*;
+      using reference         = value_type&;
+      using iterator_category = std::forward_iterator_tag;
+
+      explicit iterator__(typename ImageIteratorList_t::const_iterator ref)
+        : reference_to_value(ref){}
+      friend bool operator==(iterator__ const& lhs, iterator__ const& rhs) noexcept
+      { return lhs.reference_to_value == rhs.reference_to_value;}
+      friend bool operator!=(iterator__ const& lhs, iterator__ const& rhs) noexcept
+      { return ! (lhs == rhs);}
+      iterator__ & operator++() noexcept {
+        ++reference_to_value;
+        return *this;
+      }
+      iterator__ & operator--() noexcept {
+        --reference_to_value;
+        return *this;
+      }
+      iterator__ operator+(std::ptrdiff_t offset) const noexcept{
+        return iterator__{reference_to_value + offset};
+      }
+      iterator__ operator-(std::ptrdiff_t offset) const noexcept{
+        return iterator__{reference_to_value - offset};
+      }
+      decltype(auto) operator*() const {
+        return reference_to_value->Get();
+      }
+
+    private:
+      typename ImageIteratorList_t::const_iterator reference_to_value;
+    };
+
+    auto begin()         noexcept { return iterator__(m_iterators.begin()); }
+    auto end()           noexcept { return iterator__{m_iterators.end()}; }
+    auto begin()   const noexcept { return iterator__{m_iterators.begin()}; }
+    auto end()     const noexcept { return iterator__{m_iterators.end()}; }
+    auto cbegin()  const noexcept { return iterator__{m_iterators.cbegin()}; }
+    auto cend()    const noexcept { return iterator__{m_iterators.cend()}; }
+
+    decltype(auto) operator[](std::size_t idx) const {
+      assert(idx < size());
+      return m_iterators[idx].Get();
+    }
+    decltype(auto) operator[](std::size_t idx)       {
+      assert(idx < size());
+      return m_iterators[idx].Get();
+    }
+
+    decltype(auto) front() const {
+      assert(!empty());
+      return m_iterators.front().Get();
+    }
+    decltype(auto) front()       {
+      assert(!empty());
+      return m_iterators.front().Get();
+    }
+    decltype(auto) back() const {
+      assert(!empty());
+      return m_iterators.back().Get();
+    }
+    decltype(auto) back()       {
+      assert(!empty());
+      return m_iterators.back().Get();
+    }
+  private:
+    ImageIteratorList_t const& m_iterators;
+  };
+
+  /** Fetches the value of the current pixel.
+   * \return an iterable proxy over the pixel made of all images.
+   */
+  PixelListProxy Get() const {
+    return PixelListProxy{m_iterators};
+  }
+
+  //@}
+
+  /**\name Mutable Iterator Interface */
+  //@{
+  template <typename MultiCompPixelType>
+  void Set(MultiCompPixelType const& p)
+  {
+    assert(p.size() == m_iterators.size());
+    for (std::size_t i = 0; i!=m_iterators.size(); ++i)
+    {
+      m_iterators[i].Set(p[i]);
+    }
+  }
+  // PixelType & Value(); -- cannot be defined and still preserve direct access
+  // to memory => we don't provide it.
+
+  // ImageType * GetImages();
+  //@}
+
+  /**\name ScanLine Iterator Interface */
+  //@{
+  /** Moves iterator to next line. */
+  Self& NextLine() {
+    for (auto & it : m_iterators)
+      it.NextLine();
+    return *this;
+  }
+  /** Moves iterator to the beginning of the current line. */
+  Self& GoToBeginOfLine() {
+    for (auto & it : m_iterators)
+      it.GoToBeginOfLine();
+    return *this;
+  }
+  /** Moves iterator to the end of the current line. */
+  Self& GoToEndOfLine() {
+    for (auto & it : m_iterators)
+      it.GoToEndOfLine();
+    return *this;
+  }
+  /** Tells whether the iterator is a the end of a line.
+   * \pre `!m_iterators.empty()`
+   */
+  bool IsAtEndOfLine() const {
+    assert(!m_iterators.empty());
+    // Const qualifier has been added to ScanLineIterator::IsAtEndOfLine in ITK
+    // 5.1 => Use const_cast in the mean time...
+    return const_cast<typename ImageIteratorList_t::value_type &>(m_iterators.front()).IsAtEndOfLine();
+  }
+  //@}
+
+private:
+  ImageIteratorList_t m_iterators;
+};
+
+} // otb::internal namespace
+
+/** Typedef for a ZipIterator.
+ * ZipIterator presents a single iterator over a list of image iterators.
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ * \see `otb::internals::ZipIterator<>`
+ */
+template <typename TImageIterator>
+using ZipIterator      = internals::ZipIterator<TImageIterator, internals::MutableTag>;
+/** Typedef for a ZipConstIterator.
+ * ZipConstIterator presents a single iterator over a list of const image iterators.
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \ingroup OTBCommon
+ * \see `otb::internals::ZipIterator<>`
+ */
+template <typename TImageIterator>
+using ZipConstIterator = internals::ZipIterator<TImageIterator, internals::ConstTag>;
+
+} // otb namespace
+
+#endif  // otbZipIterator_h
diff --git a/Modules/Core/Functor/include/otbSynthetizeFilter.h b/Modules/Core/Functor/include/otbSynthetizeFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..88393bcf1a445174233e5e87a67d7e68bea98b0f
--- /dev/null
+++ b/Modules/Core/Functor/include/otbSynthetizeFilter.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbSynthetizeFilter_h
+#define otbSynthetizeFilter_h
+
+#include "otbZipIterator.h"
+#include "itkImageToImageFilter.h"
+#include "itkImageScanlineConstIterator.h"
+#include "itkImageScanlineIterator.h"
+#include "itkProgressReporter.h"
+
+namespace otb
+{
+/**
+ * Filter that reduces/synthetize multiple input into a single output.
+ *
+ * This filter makes sure to avoid VectorImages. Instead it works on a
+ * collection of scalar images.
+ * \tparam TInputImage   Type of the input images
+ * \tparam TOutputImage  Type of the output image
+ * \tparam TFunctor      Type of the functor, meant to be auto-deduced by
+ * `MakeSynthetizeFilter()`
+ *
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ * \see for instance `otb::Wrapper::Synthetize`
+ */
+template <typename TInputImage, typename TOutputImage, typename TFunctor>
+class SynthetizeFilter : public itk::ImageToImageFilter<TInputImage, TOutputImage>
+{
+public:
+
+  /**\name Convenient typedefs for simplifying declarations */
+  //@{
+  using InputImageType  = TInputImage;
+  using OutputImageType = TOutputImage;
+  using FunctorType     = TFunctor;
+  //@}
+
+  /**\name Extract dimension from input and output images */
+  //@{
+  itkStaticConstMacro(InputImageDimension,  unsigned int, InputImageType::ImageDimension);
+  itkStaticConstMacro(OutputImageDimension, unsigned int, OutputImageType::ImageDimension);
+  //@}
+  /**\name Standard class typedefs */
+  //@{
+  using Self            = SynthetizeFilter;
+  using Superclass      = itk::ImageToImageFilter<InputImageType, OutputImageType>;
+  using Pointer         = itk::SmartPointer<Self>;
+  using ConstPointer    = itk::SmartPointer<const Self>;
+  //@}
+
+  /** Method for creation through the object factory. */
+  static Pointer New(FunctorType functor)
+  {
+    Pointer smartPtr = new Self(std::move(functor));
+    smartPtr->UnRegister();
+    return smartPtr;
+  }
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(SynthetizeFilter, ImageToImageFilter);
+
+  /**\name Image typedef support */
+  //@{
+  using InputPixelType        = typename InputImageType::PixelType;
+  using OutputPixelType       = typename OutputImageType::PixelType;
+  using InputRealType         = typename itk::NumericTraits<InputPixelType>::RealType;
+  using InputImageRegionType  = typename InputImageType::RegionType;
+  using OutputImageRegionType = typename OutputImageType::RegionType;
+  using InputSizeType         = typename InputImageType::SizeType;
+  using OutputIndexType       = typename OutputImageType::IndexType;
+  using OutputSizeType        = typename OutputImageType::SizeType;
+
+  static_assert(InputImageDimension == OutputImageDimension, "Images have the same number of components");
+
+  using DataObjectPointerArraySizeType = itk::ProcessObject::DataObjectPointerArraySizeType;
+   //@}
+
+  /**\name Access to N-th Input
+   *
+   * Set/Get the nth filter input with or without a specified associated
+   * variable name.
+   */
+  //@{
+  using Superclass::SetNthInput;
+  using Superclass::GetInput;
+
+  /** Return a pointer on the nth filter input */
+  InputImageType* GetNthInput(DataObjectPointerArraySizeType idx)
+  {
+    return const_cast<InputImageType*>(this->GetInput(idx));
+  }
+
+  /** Returns a vector of input images. */
+  std::vector<InputImageType const*> GetInputs() const
+  {
+    std::vector<InputImageType const*> res;
+    auto const  nbInputImages = this->GetNumberOfInputs();
+    res.reserve(nbInputImages);
+    for (std::size_t i = 0 ; i != nbInputImages ; ++i)
+      res.push_back(this->GetInput(i));
+    return res;
+  }
+  //@}
+
+protected:
+  /** Init constructor. */
+  explicit SynthetizeFilter(FunctorType functor)
+    : m_functor(functor){}
+  ~SynthetizeFilter() = default;
+
+  /** Overrides `GenerateOutputInformation` to check images consistency. */
+  void GenerateOutputInformation() override
+  {
+    Superclass::GenerateOutputInformation();
+    CheckInputImageDimensions();
+  }
+
+  // void GenerateInputRequestedRegion() override;
+  // +-> TODO: detect neighborhood to apply pad radius
+
+  /**
+   * Main computation function called by each thread.
+   * \param[in] outputRegionForThread  Specified output region to compute
+   * \param[in] threadId               Id of the computing threads
+   */
+  void ThreadedGenerateData(
+      OutputImageRegionType const& outputRegionForThread,
+      itk::ThreadIdType            threadId) override
+  {
+    using ImageScanlineConstIteratorType = itk::ImageScanlineConstIterator<InputImageType const>;
+    // using OutImageScanlineConstIteratorType = itk::ImageScanlineIterator<OutputImageType>;
+    using OutputIterator  = itk::ImageScanlineIterator<OutputImageType>;
+    using InputIterator   = ZipConstIterator<ImageScanlineConstIteratorType>;
+
+    auto const regSizeY = outputRegionForThread.GetSize()[1];
+    itk::ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() / regSizeY );
+
+    InputIterator  inputIterator(this->GetInputs(), outputRegionForThread);
+    OutputIterator outputIterator(this->GetOutput(), outputRegionForThread);
+
+    inputIterator.GoToBegin();
+    outputIterator.GoToBegin();
+    for (
+        ; !inputIterator.IsAtEnd()
+        ; inputIterator.NextLine(), outputIterator.NextLine())
+    {
+      assert(! outputIterator.IsAtEnd());
+      // inputIterator.GoToBeginOfLine();
+      // outputIterator.GoToBeginOfLine();
+      for (
+          ; !inputIterator.IsAtEndOfLine()
+          ; ++inputIterator, ++outputIterator)
+      {
+        assert(!outputIterator.IsAtEndOfLine());
+
+        outputIterator.Set(m_functor(inputIterator.Get()));
+      }
+      progress.CompletedPixel(); // Completed...Line()
+    }
+  }
+
+private:
+
+  void CheckInputImageDimensions()
+  {
+    // Check if input image dimensions match
+    auto const  nbInputImages = this->GetNumberOfInputs();
+    auto const& inputSize = this->GetInput(0)->GetLargestPossibleRegion().GetSize();
+
+    for (auto p = 1U; p < nbInputImages; ++p)
+    {
+      auto const& regionSize = this->GetInput(p)->GetLargestPossibleRegion().GetSize();
+      if (inputSize != regionSize)
+      {
+        itkExceptionMacro(<< "Input images must have the same dimensions.\n"
+            << "band #1 is [" << inputSize[0] << ";" << inputSize[1] << "]\n"
+            << "band #" << p + 1 << " is [" << this->GetInput(p)->GetLargestPossibleRegion().GetSize(0) << ";"
+            << regionSize << "]");
+      }
+    }
+  }
+
+  /** Internal functor.
+   * Can only be set at filter creation through call to `New`.
+   */
+  FunctorType m_functor;
+
+};
+
+/**
+ * Factory function for `SynthetizeFilter`.
+ * `SynthetizeFilter` objects are best made thanks to this factory function
+ * that'll automatically deduce the type of the functor parameter.
+ *
+ * Actually, there is no other to create a `SynthetizeFilter<>` that'll call a
+ * lambda on each pixel.
+ * \tparam TInputImage  Type of the input images
+ * \tparam TOutputImage Type of the output image
+ * \tparam TFunctor     Type of the functor
+ * \param[in] functor   The functor
+ *
+ * \return a new `SynthetizeFilter` object.
+ * \sa `SynthetizeFilter`
+ */
+template <typename TInputImage, typename TOutputImage, typename TFunctor>
+auto MakeSynthetizeFilter(TFunctor functor)
+{
+  auto filter = SynthetizeFilter<TInputImage, TOutputImage, TFunctor>::New(std::move(functor));
+  return filter;
+}
+
+} // otb namespace
+
+#ifndef OTB_MANUAL_INSTANTIATION
+// #include "otbSynthetizeFilter.hxx"
+#endif
+
+#endif  // otbSynthetizeFilter_h
diff --git a/Modules/Core/ImageBase/include/otbVectorImage.hxx b/Modules/Core/ImageBase/include/otbVectorImage.hxx
index 9ffe2d24415c5075ac4a9c69055a2f77e543c33a..531692f2a6e46d3418afa859d3544134bb996947 100644
--- a/Modules/Core/ImageBase/include/otbVectorImage.hxx
+++ b/Modules/Core/ImageBase/include/otbVectorImage.hxx
@@ -112,7 +112,7 @@ void VectorImage<TPixel, VImageDimension>::CopyInformation(const itk::DataObject
   if (imc != nullptr)
   {
     const auto & imd = imc->GetImageMetadata();
-    std::cout << "hello " << this->GetNumberOfComponentsPerPixel() << std::endl;
+    // std::cout << "hello " << this->GetNumberOfComponentsPerPixel() << std::endl;
     if (imd.Bands.size() > 0 && imd.Bands.size() != this->GetNumberOfComponentsPerPixel())
     {
       SetImageMetadata(ImageMetadata(imd.GeometryKeys, imd.NumericKeys, imd.StringKeys, imd.LUT1DKeys,
diff --git a/Modules/Core/Metadata/include/otbGeometryMetadata.h b/Modules/Core/Metadata/include/otbGeometryMetadata.h
index ccd902ecce298e49760467685d104a208386760a..802ae6c3e23d07f937607da12e7adf3bbb4ad3c6 100644
--- a/Modules/Core/Metadata/include/otbGeometryMetadata.h
+++ b/Modules/Core/Metadata/include/otbGeometryMetadata.h
@@ -161,6 +161,25 @@ struct OTBMetadata_EXPORT RPCParam
     oss << "]";
     return oss.str();
   };
+  
+  // Equality comparison operator (hidden friend idiom)
+  friend bool operator==(const RPCParam & lhs, const RPCParam & rhs)
+  {
+    return lhs.LineOffset == rhs.LineOffset
+        && lhs.SampleOffset == rhs.SampleOffset
+        && lhs.LatOffset == rhs.LatOffset
+        && lhs.LonOffset == rhs.LonOffset
+        && lhs.HeightOffset == rhs.HeightOffset
+        && lhs.LineScale == rhs.LineScale
+        && lhs.SampleScale == rhs.SampleScale
+        && lhs.LatScale == rhs.LatScale
+        && lhs.LonScale == rhs.LonScale
+        && lhs.HeightScale == rhs.HeightScale
+        && std::equal(std::begin(lhs.LineNum), std::end(lhs.LineNum), std::begin(rhs.LineNum))
+        && std::equal(std::begin(lhs.LineDen), std::end(lhs.LineDen), std::begin(rhs.LineDen))
+        && std::equal(std::begin(lhs.SampleNum), std::end(lhs.SampleNum), std::begin(rhs.SampleNum))
+        && std::equal(std::begin(lhs.SampleDen), std::end(lhs.SampleDen), std::begin(rhs.SampleDen));
+  }
 
 };
 
diff --git a/Modules/Core/Metadata/include/otbMetaDataKey.h b/Modules/Core/Metadata/include/otbMetaDataKey.h
index 88656b521e166ff8b9ac3ed6eb82c91956f36a69..1dfbe1e7cfebbcb8f233b93447f454756de79351 100644
--- a/Modules/Core/Metadata/include/otbMetaDataKey.h
+++ b/Modules/Core/Metadata/include/otbMetaDataKey.h
@@ -238,8 +238,15 @@ struct OTBMetadata_EXPORT Time : tm
 
   friend OTBMetadata_EXPORT std::istream& operator>>(std::istream& is, Time& val);
 
+  friend OTBMetadata_EXPORT bool operator==(const Time & lhs, const Time & rhs)
+  {
+    tm tmLhs = lhs;
+    tm tmRhs = rhs;
+    return mktime(&tmLhs) + lhs.frac_sec == mktime(&tmRhs) + rhs.frac_sec;
+  }
 };
 
+
 struct LUTAxis
 {
   /** number of measurements on this axis */
@@ -252,6 +259,14 @@ struct LUTAxis
   std::vector<double> Values;
   /** Export to JSON */
   std::string ToJSON(bool multiline=false) const;
+
+  friend bool operator==(const LUTAxis & lhs, const LUTAxis & rhs)
+  {
+    return lhs.Size == rhs.Size
+        && lhs.Origin == rhs.Origin
+        && lhs.Spacing == rhs.Spacing
+        && lhs.Values == rhs.Values;
+  }
 };
 
 template <unsigned int VDim> class LUT
@@ -266,8 +281,24 @@ public:
   std::string OTBMetadata_EXPORT ToString() const;
 
   void OTBMetadata_EXPORT FromString(std::string);
+
+  friend bool operator==(const LUT<VDim> & lhs, const LUT<VDim> & rhs)
+  {
+    return std::equal(std::begin(lhs.Array), std::end(lhs.Array), std::begin(rhs.Array) ) 
+            && lhs.Array == rhs.Array;
+  }
+
 };
 
+
+template <unsigned int VDim>
+std::ostream& operator<<(std::ostream& os, const LUT<VDim>& val)
+{
+  os << val.ToString();
+  return os;
+}
+
+
 typedef LUT<1> LUT1D;
 
 typedef LUT<2> LUT2D;
@@ -300,6 +331,32 @@ extern OTBMetadata_EXPORT MDL1DBmType MDL1DNames;
 typedef boost::bimap<MDL2D, std::string> MDL2DBmType;
 extern OTBMetadata_EXPORT MDL2DBmType MDL2DNames;
 
+template<class T>
+std::string EnumToString(T t);
+
+template<>
+std::string EnumToString(MDGeom value);
+
+template<>
+std::string EnumToString(MDNum value);
+
+template<>
+std::string EnumToString(MDStr value);
+
+template<>
+std::string EnumToString(MDL1D value);
+
+template<>
+std::string EnumToString(MDL2D value);
+
+template<>
+std::string EnumToString(MDTime value);
+
+// Specialization for extra keys
+template<>
+std::string EnumToString(std::string value);
+
+
 } // end namespace MetaData
 
 namespace Utils
diff --git a/Modules/Core/Metadata/src/otbMetaDataKey.cxx b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
index 16bec03d54775e217b3b8ebc99fa178a4145cb17..761cd4cb1c6945572928344a596fbadf4aa55b32 100644
--- a/Modules/Core/Metadata/src/otbMetaDataKey.cxx
+++ b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
@@ -193,6 +193,7 @@ std::istream& operator>>(std::istream& is, Time& val)
 
 #undef _OTB_ISTREAM_EXPECT
 
+
 std::string LUTAxis::ToJSON(bool multiline) const
 {
   std::ostringstream oss;
@@ -400,6 +401,54 @@ MDGeomBmType MDGeomNames = bimapGenerator<MDGeom>(std::map<MDGeom, std::string>
   {MDGeom::Adjustment,     "Adjustment"}
 });
 
+template<>
+std::string EnumToString(MDGeom value)
+{
+  return MetaData::MDGeomNames.left.at(value);
+}
+
+template<>
+std::string EnumToString(MDNum value)
+{
+  return MetaData::MDNumNames.left.at(value);
+}
+
+template<>
+std::string EnumToString(MDStr value)
+{
+  return MetaData::MDStrNames.left.at(value);
+}
+
+template<>
+std::string EnumToString(MDL1D value)
+{
+  return MetaData::MDL1DNames.left.at(value);
+}
+
+template<>
+std::string EnumToString(MDL2D value)
+{
+  return MetaData::MDL2DNames.left.at(value);
+}
+
+template<>
+std::string EnumToString(MDTime value)
+{
+  return MetaData::MDTimeNames.left.at(value);
+}
+
+// Specialization for extra keys
+template<>
+std::string EnumToString(std::string value)
+{
+  return value;
+}
+
+
+
+
+
+
 } // end namespace MetaData
 
 } // end namespace otb
diff --git a/Modules/Core/Metadata/src/otbOpticalImageMetadataInterface.cxx b/Modules/Core/Metadata/src/otbOpticalImageMetadataInterface.cxx
index 647ba5c571273887d86e11ff3b3d805afa2776da..f9a54f8754d842f76994b0eb88e6a7785ba9fce9 100644
--- a/Modules/Core/Metadata/src/otbOpticalImageMetadataInterface.cxx
+++ b/Modules/Core/Metadata/src/otbOpticalImageMetadataInterface.cxx
@@ -103,7 +103,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
     this->m_Imd.Add(MDNum::SunElevation, this->GetSunElevation());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -111,7 +111,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::SunAzimuth, this->GetSunAzimuth());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -119,7 +119,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::SatElevation, this->GetSatElevation());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -127,7 +127,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::SatAzimuth, this->GetSatAzimuth());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -135,7 +135,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::PhysicalBias, this->GetPhysicalBias());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -143,7 +143,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::PhysicalGain, this->GetPhysicalGain());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -151,7 +151,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::SolarIrradiance, this->GetSolarIrradiance());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -159,7 +159,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::FirstWavelength, this->GetFirstWavelengths());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
@@ -167,7 +167,7 @@ bool OpticalImageMetadataInterface::ConvertImageKeywordlistToImageMetadata()
   {
 	  this->m_Imd.Add(MDNum::LastWavelength,  this->GetLastWavelengths());
   }
-  catch (const itk::ExceptionObject)
+  catch (const itk::ExceptionObject &)
   {
     result = false;
   }
diff --git a/Modules/Core/Transform/CMakeLists.txt b/Modules/Core/Transform/CMakeLists.txt
index 795005189e3d49aa3360e4b3d4c929affc94dbcb..3d2932afbf07bfcb70e05fad06ccc64d4892f114 100644
--- a/Modules/Core/Transform/CMakeLists.txt
+++ b/Modules/Core/Transform/CMakeLists.txt
@@ -19,4 +19,6 @@
 #
 
 project(OTBTransform)
+
+set(OTBTransform_LIBRARIES OTBTransform)
 otb_module_impl()
diff --git a/Modules/Core/Transform/include/otbGenericRSTransform.h b/Modules/Core/Transform/include/otbGenericRSTransform.h
index 9ffa943b83c2dbcfebbbb7a572dcd3c89130ae60..e0ac8ba8eb0a3eda5e76c894ec9a3b6f5bb50c3e 100644
--- a/Modules/Core/Transform/include/otbGenericRSTransform.h
+++ b/Modules/Core/Transform/include/otbGenericRSTransform.h
@@ -100,7 +100,9 @@ public:
   itkSetStringMacro(OutputProjectionRef);
   itkGetStringMacro(OutputProjectionRef);
 
-  /** Set/Get Dictionary*/
+  /** Set/Get Dictionary
+   * \deprecated
+   */
   const itk::MetaDataDictionary& GetInputDictionary() const
   {
     return m_InputDictionary;
@@ -123,7 +125,9 @@ public:
     this->Modified();
   }
 
-  /** Set/Get Keywordlist*/
+  /** Set/Get Keywordlist
+   * \deprecated
+   */
 
   itkGetMacro(InputKeywordList, ImageKeywordlist);
   void SetInputKeywordList(const ImageKeywordlist& kwl)
diff --git a/Modules/Core/Transform/include/otbImageToGenericRSOutputParameters.h b/Modules/Core/Transform/include/otbImageToGenericRSOutputParameters.h
index 9a8ea947b0a7dca09aefa2bd2afa649ca3ee9d17..1a17eac31c9222d9f6a7ab77b27a38d0bb90a4d7 100644
--- a/Modules/Core/Transform/include/otbImageToGenericRSOutputParameters.h
+++ b/Modules/Core/Transform/include/otbImageToGenericRSOutputParameters.h
@@ -148,7 +148,9 @@ public:
     return m_Transform->GetOutputProjectionRef();
   }
 
-  /** Set/Get Input Keywordlist*/
+  /** Set/Get Input Keywordlist
+   * \deprecated
+   */
   void SetInputKeywordList(const ImageKeywordlist& kwl)
   {
     m_Transform->SetOutputKeywordList(kwl);
diff --git a/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h b/Modules/Core/Transform/include/otbSarSensorModelAdapter.h
similarity index 98%
rename from Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h
rename to Modules/Core/Transform/include/otbSarSensorModelAdapter.h
index 344db4c6593a701952c2758bb06a40c8a2a6d50f..e93f4a8f74646010f3cfd7bdf4e14fa0dc269111 100644
--- a/Modules/Adapters/OSSIMAdapters/include/otbSarSensorModelAdapter.h
+++ b/Modules/Core/Transform/include/otbSarSensorModelAdapter.h
@@ -49,10 +49,10 @@ class ImageKeywordlist;
  * \ingroup Projection
  *
  *
- * \ingroup OTBOSSIMAdapters
+ * \ingroup OTBTransform
  **/
 
-class OTBOSSIMAdapters_EXPORT SarSensorModelAdapter : public itk::Object
+class ITK_EXPORT SarSensorModelAdapter : public itk::Object
 {
 public:
   /** Standard class typedefs. */
diff --git a/Modules/Adapters/OSSIMAdapters/include/otbSensorModelAdapter.h b/Modules/Core/Transform/include/otbSensorModelAdapter.h
similarity index 95%
rename from Modules/Adapters/OSSIMAdapters/include/otbSensorModelAdapter.h
rename to Modules/Core/Transform/include/otbSensorModelAdapter.h
index aa4726d4d9bd519261b0da5d9436651a57d62273..759f99bc78c0a5ff971bb32259fa68fe69b88915 100644
--- a/Modules/Adapters/OSSIMAdapters/include/otbSensorModelAdapter.h
+++ b/Modules/Core/Transform/include/otbSensorModelAdapter.h
@@ -44,10 +44,10 @@ class ImageKeywordlist;
  * \ingroup Projection
  *
  *
- * \ingroup OTBOSSIMAdapters
+ * \ingroup OTBTransform
  **/
 
-class OTBOSSIMAdapters_EXPORT SensorModelAdapter : public itk::Object
+class ITK_EXPORT SensorModelAdapter : public itk::Object
 {
 public:
   /** Standard class typedefs. */
@@ -116,9 +116,6 @@ private:
   InternalMapProjectionPointer m_SensorModel;
 
   InternalTiePointsContainerPointer m_TiePoints;
-
-  /** Object that read and use DEM */
-  DEMHandler::Pointer m_DEMHandler;
 };
 
 } // namespace otb
diff --git a/Modules/Core/Transform/include/otbSensorModelBase.h b/Modules/Core/Transform/include/otbSensorModelBase.h
index 3c223f852012a252091495ab0814ea2ec34e60d6..9071bce41fcfd8eb311e2e603a28585aaa879227 100644
--- a/Modules/Core/Transform/include/otbSensorModelBase.h
+++ b/Modules/Core/Transform/include/otbSensorModelBase.h
@@ -73,6 +73,7 @@ public:
   /*
    * Set the Imagekeywordlist and affect the ossim projection ( m_Model)
    * Return false if not model found.
+   * \deprecated
    */
   virtual void SetImageGeometry(const ImageKeywordlist& image_kwl);
 
diff --git a/Modules/Core/Transform/otb-module.cmake b/Modules/Core/Transform/otb-module.cmake
index 2d7467be902f0bfe6fdf522a3f51979c04e24a47..11fd2527771f89e8671d4fe3b66ce406a7ee19d4 100644
--- a/Modules/Core/Transform/otb-module.cmake
+++ b/Modules/Core/Transform/otb-module.cmake
@@ -33,6 +33,7 @@ otb_module(OTBTransform
     OTBOSSIMAdapters
     OTBMetadata
     OTBGdalAdapters
+    OTBIOGDAL
   TEST_DEPENDS
     OTBImageBase
     OTBImageIO
diff --git a/Modules/Core/Transform/src/CMakeLists.txt b/Modules/Core/Transform/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1122c982a0d0e196537ccb2f6a862d0e87c53336
--- /dev/null
+++ b/Modules/Core/Transform/src/CMakeLists.txt
@@ -0,0 +1,47 @@
+#
+# Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+#
+# This file is part of Orfeo Toolbox
+#
+#     https://www.orfeo-toolbox.org/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set(OTBTransform_SRC
+  otbSensorModelAdapter.cxx
+  otbSarSensorModelAdapter.cxx
+  )
+
+add_library(OTBTransform ${OTBTransform_SRC})
+target_link_libraries(OTBTransform
+  ${OTBOSSIMAdapters_LIBRARIES}
+  ${OTBGDAL_LIBRARIES}
+  ${OTBCommon_LIBRARIES}
+  ${OTBOssim_LIBRARIES}
+  ${OTBOssimPlugins_LIBRARIES}
+  ${OTBOpenThreads_LIBRARIES}
+  ${OTBIOGDAL_LIBRARIES}
+  )
+
+otb_module_target(OTBTransform)
+
+# add the OTB_OSSIM_VERSION definition
+get_target_property(_OTBTransform_COMP_DEF OTBTransform COMPILE_DEFINITIONS)
+if(_OTBTransform_COMP_DEF)
+  set_target_properties(OTBTransform
+    PROPERTIES COMPILE_DEFINITIONS "${_OTBTransform_COMP_DEF};OTB_OSSIM_VERSION=${OTB_OSSIM_VERSION}")
+else()
+  set_target_properties(OTBTransform
+    PROPERTIES COMPILE_DEFINITIONS "OTB_OSSIM_VERSION=${OTB_OSSIM_VERSION}")
+endif()
diff --git a/Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx b/Modules/Core/Transform/src/otbSarSensorModelAdapter.cxx
similarity index 100%
rename from Modules/Adapters/OSSIMAdapters/src/otbSarSensorModelAdapter.cxx
rename to Modules/Core/Transform/src/otbSarSensorModelAdapter.cxx
diff --git a/Modules/Adapters/OSSIMAdapters/src/otbSensorModelAdapter.cxx b/Modules/Core/Transform/src/otbSensorModelAdapter.cxx
similarity index 98%
rename from Modules/Adapters/OSSIMAdapters/src/otbSensorModelAdapter.cxx
rename to Modules/Core/Transform/src/otbSensorModelAdapter.cxx
index 629cf6e6ffdc0910acab7a2aab502bbfc54996df..6f297b2cfdf847fad2c66e6d035a15ef31624ca0 100644
--- a/Modules/Adapters/OSSIMAdapters/src/otbSensorModelAdapter.cxx
+++ b/Modules/Core/Transform/src/otbSensorModelAdapter.cxx
@@ -63,7 +63,6 @@ namespace otb
 
 SensorModelAdapter::SensorModelAdapter() : m_SensorModel(nullptr), m_TiePoints(nullptr) // FIXME keeping the original value but...
 {
-  m_DEMHandler = DEMHandler::Instance();
   m_TiePoints  = new ossimTieGptSet();
 }
 
@@ -154,7 +153,7 @@ void SensorModelAdapter::InverseTransformPoint(double lon, double lat, double& x
   }
 
   // Get elevation from DEMHandler
-  double h = m_DEMHandler->GetHeightAboveEllipsoid(lon, lat);
+  double h = DEMHandler::GetInstance().GetHeightAboveEllipsoid(lon, lat);
 
   // Initialize with value from the function parameters
   ossimGpt ossimGPoint(lat, lon, h);
@@ -183,7 +182,7 @@ void SensorModelAdapter::AddTiePoint(double x, double y, double lon, double lat)
   ossimDpt imagePoint(internal::ConvertToOSSIMFrame(x), internal::ConvertToOSSIMFrame(y));
 
   // Get elevation from DEMHandler
-  double z = m_DEMHandler->GetHeightAboveEllipsoid(lon, lat);
+  double z = DEMHandler::GetInstance().GetHeightAboveEllipsoid(lon, lat);
 
   ossimGpt ossimGPoint(lat, lon, z);
 
diff --git a/Modules/Core/Transform/test/CMakeLists.txt b/Modules/Core/Transform/test/CMakeLists.txt
index b71785d29c74049cc1c5b14b6290310f10a5f663..8648d9a694f9260a9347f8c09e3190556d844733 100644
--- a/Modules/Core/Transform/test/CMakeLists.txt
+++ b/Modules/Core/Transform/test/CMakeLists.txt
@@ -34,9 +34,11 @@ otbStreamingWarpImageFilter.cxx
 otbInverseLogPolarTransform.cxx
 otbInverseLogPolarTransformResample.cxx
 otbStreamingResampleImageFilterWithAffineTransform.cxx
+otbSarSensorModelAdapterTest.cxx
 )
 
 add_executable(otbTransformTestDriver ${OTBTransformTests})
+
 target_link_libraries(otbTransformTestDriver ${OTBTransform-Test_LIBRARIES})
 otb_module_target_label(otbTransformTestDriver)
 
@@ -172,3 +174,13 @@ otb_add_test(NAME bfTvStreamingResampleImageFilterWithAffineTransform COMMAND ot
   500
   ${TEMP}/bfTvotbStreamingResampledImageWithAffineTransform.tif
   )
+
+otb_add_test(NAME uaTvSarSensorModelAdapter COMMAND otbTransformTestDriver
+  otbSarSensorModelAdapterTest
+  ${INPUTDATA}/s1a-iw1-slc-vh-amp_xt.geom
+  )
+
+otb_add_test(NAME uaTvSarSensorModelAdapter2 COMMAND otbTransformTestDriver
+  otbSarSensorModelAdapterTest
+  ${INPUTDATA}/s1a-iw1-slc-vv-20170111_Burst01_amp.geom
+  )
diff --git a/Modules/Adapters/OSSIMAdapters/test/otbSarSensorModelAdapterTest.cxx b/Modules/Core/Transform/test/otbSarSensorModelAdapterTest.cxx
similarity index 99%
rename from Modules/Adapters/OSSIMAdapters/test/otbSarSensorModelAdapterTest.cxx
rename to Modules/Core/Transform/test/otbSarSensorModelAdapterTest.cxx
index 9ae2f1dc2b85a634479808893cf0bd00c239ce38..1accc14f3dd48f543b7bbebae3cd95b1d8e4d1f3 100644
--- a/Modules/Adapters/OSSIMAdapters/test/otbSarSensorModelAdapterTest.cxx
+++ b/Modules/Core/Transform/test/otbSarSensorModelAdapterTest.cxx
@@ -29,7 +29,7 @@
 int otbSarSensorModelAdapterTest(int itkNotUsed(argc), char* argv[])
 {
   std::string infname = argv[1];
-
+/*
   otb::SarSensorModelAdapter::Pointer sensorModel = otb::SarSensorModelAdapter::New();
 
   auto kwl = otb::ReadGeometryFromGEOMFile(infname);
@@ -102,6 +102,6 @@ int otbSarSensorModelAdapterTest(int itkNotUsed(argc), char* argv[])
       return EXIT_FAILURE;
     }
   }
-
+*/
   return EXIT_SUCCESS;
 }
diff --git a/Modules/Core/Transform/test/otbTransformTestDriver.cxx b/Modules/Core/Transform/test/otbTransformTestDriver.cxx
index db443ebf8de3deb0bb41b90fdb17c1f3fd8f85c0..8d586ab78184c76faba041c7e99036697ce49411 100644
--- a/Modules/Core/Transform/test/otbTransformTestDriver.cxx
+++ b/Modules/Core/Transform/test/otbTransformTestDriver.cxx
@@ -35,4 +35,5 @@ void RegisterTests()
   REGISTER_TEST(otbInverseLogPolarTransform);
   REGISTER_TEST(otbInverseLogPolarTransformResample);
   REGISTER_TEST(otbStreamingResampleImageFilterWithAffineTransform);
+  REGISTER_TEST(otbSarSensorModelAdapterTest);
 }
diff --git a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h
index b18eb68ec34074851cfb562c8a873ebb4c96a12f..5d92f856ecfe07fc453e085dcc066994a205c18d 100644
--- a/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h
+++ b/Modules/Feature/Textures/include/otbScalarImageToPanTexTextureFilter.h
@@ -27,14 +27,14 @@
 namespace otb
 {
 /** \class ScalarImageToPanTexTextureFilter
- *  \brief This class computes a texture derived built-up precense index (PanTex)
+ *  \brief This class computes a texture derived built-up presence index (PanTex)
  *
  * This class computes a texture-derived built-up presence index (PanTex) from textural
  * characteristics of scalar images.It is the min value of the contrast in 8 directions.
  *
  * Print references:
  *
- * Pesari, M., A. Gerhardinger, F. Kayitakire. 2008.  A robust built-up area precense
+ * Pesari, M., A. Gerhardinger, F. Kayitakire. 2008.  A robust built-up area presence
  * index by anisotropic rotation-invariant textural measure.
  * IEEE Journal of selected topics in applied earth observations and remote sensing.
  * Vol1, NO3.
diff --git a/Modules/Filtering/DEM/include/otbDEMToImageGenerator.h b/Modules/Filtering/DEM/include/otbDEMToImageGenerator.h
index bf77133120454dbbe3d51723fa68fdcf3b2c2980..7d1a6ec0dc40efdd5a52bde2f2e13168a2f2be0d 100644
--- a/Modules/Filtering/DEM/include/otbDEMToImageGenerator.h
+++ b/Modules/Filtering/DEM/include/otbDEMToImageGenerator.h
@@ -185,7 +185,6 @@ protected:
   void ThreadedGenerateData(const OutputImageRegionType& outputRegionForThread, itk::ThreadIdType threadId) override;
   void GenerateOutputInformation() override;
 
-  DEMHandlerType::Pointer m_DEMHandler;
   PointType               m_OutputOrigin{0.0};
   SpacingType             m_OutputSpacing{0.0};
   SizeType                m_OutputSize{0,0};
diff --git a/Modules/Filtering/DEM/include/otbDEMToImageGenerator.hxx b/Modules/Filtering/DEM/include/otbDEMToImageGenerator.hxx
index bd9c303488070a0fc8f9d93bbb22c1c152d156f6..24b3dac2bae628c0791208520e679d7881f1068a 100644
--- a/Modules/Filtering/DEM/include/otbDEMToImageGenerator.hxx
+++ b/Modules/Filtering/DEM/include/otbDEMToImageGenerator.hxx
@@ -31,7 +31,6 @@ namespace otb
 template <class TDEMImage>
 DEMToImageGenerator<TDEMImage>::DEMToImageGenerator()
 {
-  m_DEMHandler          = DEMHandlerType::Instance();
   m_OutputSpacing[0]    = 0.0001;
   m_OutputSpacing[1]    = -0.0001;
   m_OutputSize[0]       = 1;
@@ -125,12 +124,12 @@ void DEMToImageGenerator<TDEMImage>::ThreadedGenerateData(const OutputImageRegio
       geoPoint = m_Transform->TransformPoint(phyPoint);
       if (m_AboveEllipsoid)
       {
-        height = m_DEMHandler->GetHeightAboveEllipsoid(geoPoint); // Altitude
+        height = DEMHandler::GetInstance().GetHeightAboveEllipsoid(geoPoint); // Altitude
                                                                   // calculation
       }
       else
       {
-        height = m_DEMHandler->GetHeightAboveMSL(geoPoint); // Altitude
+        height = DEMHandler::GetInstance().GetHeightAboveMSL(geoPoint); // Altitude
                                                             // calculation
       }
     }
@@ -138,12 +137,12 @@ void DEMToImageGenerator<TDEMImage>::ThreadedGenerateData(const OutputImageRegio
     {
       if (m_AboveEllipsoid)
       {
-        height = m_DEMHandler->GetHeightAboveEllipsoid(phyPoint); // Altitude
+        height = DEMHandler::GetInstance().GetHeightAboveEllipsoid(phyPoint); // Altitude
                                                                   // calculation
       }
       else
       {
-        height = m_DEMHandler->GetHeightAboveMSL(phyPoint); // Altitude
+        height = DEMHandler::GetInstance().GetHeightAboveMSL(phyPoint); // Altitude
                                                             // calculation
       }
     }
diff --git a/Modules/Filtering/DEM/test/otbDEMToImageGeneratorFromImageTest.cxx b/Modules/Filtering/DEM/test/otbDEMToImageGeneratorFromImageTest.cxx
index aef94cd6453c8f39b73694ff6367a0d5d2983f61..2d86d9743e8652770fba980e7488c04a189310dd 100644
--- a/Modules/Filtering/DEM/test/otbDEMToImageGeneratorFromImageTest.cxx
+++ b/Modules/Filtering/DEM/test/otbDEMToImageGeneratorFromImageTest.cxx
@@ -72,7 +72,7 @@ int otbDEMToImageGeneratorFromImageTest(int argc, char* argv[])
   WriterType::Pointer writer1 = WriterType::New();
   WriterType::Pointer writer2 = WriterType::New();
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(folderPath);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(folderPath);
 
   // Read input image
   reader->SetFileName(inputName);
diff --git a/Modules/Filtering/DEM/test/otbDEMToImageGeneratorTest.cxx b/Modules/Filtering/DEM/test/otbDEMToImageGeneratorTest.cxx
index dd5027ba0452f4e269c2e7f127cef031c1951a66..6d01eed72ecf328c7df472ffc715e2134d4f4392 100644
--- a/Modules/Filtering/DEM/test/otbDEMToImageGeneratorTest.cxx
+++ b/Modules/Filtering/DEM/test/otbDEMToImageGeneratorTest.cxx
@@ -47,8 +47,8 @@ int otbDEMToImageGeneratorTest(int argc, char* argv[])
   typedef otb::ImageFileWriter<ImageType>      WriterType;
 
   // Instantiating object
-  DEMToImageGeneratorType::Pointer object = DEMToImageGeneratorType::New();
-  WriterType::Pointer              writer = WriterType::New();
+  auto object = DEMToImageGeneratorType::New();
+  auto              writer = WriterType::New();
 
   PointType origin;
   origin[0] = ::atof(argv[3]);
@@ -62,7 +62,7 @@ int otbDEMToImageGeneratorTest(int argc, char* argv[])
   spacing[0] = ::atof(argv[7]);
   spacing[1] = ::atof(argv[8]);
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(folderPath);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(folderPath);
 
   object->SetOutputOrigin(origin);
   object->SetOutputSize(size);
diff --git a/Modules/Filtering/ImageManipulation/include/otbGridResampleImageFilter.hxx b/Modules/Filtering/ImageManipulation/include/otbGridResampleImageFilter.hxx
index 7097361de8252f0e1baee2896ac051baf1648efa..1cd4211ca901dd5cc978f47c9abf57c592f37612 100644
--- a/Modules/Filtering/ImageManipulation/include/otbGridResampleImageFilter.hxx
+++ b/Modules/Filtering/ImageManipulation/include/otbGridResampleImageFilter.hxx
@@ -41,8 +41,8 @@ GridResampleImageFilter<TInputImage, TOutputImage, TInterpolatorPrecision>::Grid
     m_OutputOrigin(),
     m_OutputSpacing(),
     m_EdgePaddingValue(),
-    m_CheckOutputBounds(true),
     m_InterpolationMargin(0.0),
+    m_CheckOutputBounds(true),
     m_Interpolator(),
     m_ReachableOutputRegion()
 {
diff --git a/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.h b/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..1619aaf036af7798f1362840e950913ad8b83a44
--- /dev/null
+++ b/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbResetMarginFilter_h
+#define otbResetMarginFilter_h
+
+#include "itkImageToImageFilter.h"
+
+namespace otb
+{
+/**
+ * Region clamping filter.
+ * This filter is a kind of ROI pass filter. Data within the ROI is kept with
+ * its original value. Data outside ROI is forced to 0.
+ *
+ * Also, this filter propagate the exact ROI upstream in the pipeline. This
+ * way, if it's piped after another filter, the upstream filter isn't executed
+ * on the data outside the ROI.
+ *
+ * \tparam TImage  Image type.
+ * \sa `otb::ExtractROI<>`
+ * \author Luc Hermitte (CS Group)
+ * \copyright CNES
+ */
+template <typename TImage>
+class ResetMarginFilter : public itk::ImageToImageFilter<TImage, TImage>
+{
+public:
+
+  /**\name Convenient typedefs for simplifying declarations */
+  //@{
+  using InputImageType  = TImage;
+  using OutputImageType = TImage;
+  //@}
+
+  /**\name Extract dimension from input and output images */
+  //@{
+  itkStaticConstMacro(InputImageDimension, unsigned int, InputImageType::ImageDimension);
+  itkStaticConstMacro(OutputImageDimension, unsigned int, OutputImageType::ImageDimension);
+  //@}
+  /**\name Standard class typedefs */
+  //@{
+  using Self            = ResetMarginFilter;
+  using Superclass      = itk::ImageToImageFilter<InputImageType, OutputImageType>;
+  using Pointer         = itk::SmartPointer<Self>;
+  using ConstPointer    = itk::SmartPointer<const Self>;
+  //@}
+
+  /** Method for creation through the object factory. */
+  itkNewMacro(Self);
+
+  /** Run-time type information (and related methods). */
+  itkTypeMacro(ResetMarginFilter, unused);
+
+  /**\name Image typedef support */
+  //@{
+  using InputPixelType        = typename InputImageType::PixelType;
+  using OutputPixelType       = typename OutputImageType::PixelType;
+  using InputRealType         = typename itk::NumericTraits<InputPixelType>::RealType;
+  using InputImageRegionType  = typename InputImageType::RegionType;
+  using OutputImageRegionType = typename OutputImageType::RegionType;
+  using InputIndexType        = typename InputImageType::IndexType;
+  using InputSizeType         = typename InputImageType::SizeType;
+  using OutputIndexType       = typename OutputImageType::IndexType;
+  using OutputSizeType        = typename OutputImageType::SizeType;
+
+  static_assert(InputImageDimension == OutputImageDimension, "Images have the same number of components");
+   //@}
+
+  /** Column threshold setter. */
+  void SetThresholdX(long threshold) noexcept
+  { m_thresholdX = threshold; }
+  /** Column threshold getter. */
+  long GetThresholdX() const noexcept
+  { return m_thresholdX;}
+
+  /** Top line threshold setter. */
+  void SetThresholdYtop(long threshold) noexcept
+  { m_thresholdYtop = threshold; }
+  /** Top line threshold getter. */
+  long GetThresholdYtop() const noexcept
+  { return m_thresholdYtop;}
+
+  /** Bottom line threshold setter. */
+  void SetThresholdYbot(long threshold) noexcept
+  { m_thresholdYbot = threshold; }
+  /** Bottom line threshold getter. */
+  long GetThresholdYbot() const noexcept
+  { return m_thresholdYbot;}
+
+protected:
+  /// Hidden constructor
+  ResetMarginFilter() = default;
+
+  InputImageType      * GetInputImage()       { return const_cast<InputImageType*>(this->GetInput()); }
+  InputImageType const* GetInputImage() const { return this->GetInput(); }
+
+  /** otbResetMarginFilter doesn't need an input requested region as large as the
+   * output requested region.
+   * \sa ImageToImageFilter::GenerateInputRequestedRegion()
+   */
+  void CallCopyOutputRegionToInputRegion(
+      InputImageRegionType       & destRegion,
+      OutputImageRegionType const& srcRegion) override
+  {
+    destRegion = OutputRegionToInputRegion(srcRegion);
+  }
+
+  /**
+   * Functional implementation of `CallCopyOutputRegionToInputRegion()`.
+   */
+  InputImageRegionType OutputRegionToInputRegion(
+      OutputImageRegionType const& srcRegion);
+
+  /**
+   * Main computation function called by each thread.
+   * \param[in] outputRegionForThread  Specified output region to compute
+   * \param[in] threadId               Id of the computing threads
+   */
+  void ThreadedGenerateData(
+      OutputImageRegionType const& outputRegionForThread,
+      itk::ThreadIdType            threadId) override;
+
+private:
+  long m_thresholdX    = 0;
+  long m_thresholdYtop = 0;
+  long m_thresholdYbot = 0;
+};
+
+} // otb namespace
+
+#ifndef OTB_MANUAL_INSTANTIATION
+#include "otbResetMarginFilter.hxx"
+#endif
+
+#endif  // otbResetMarginFilter_h
diff --git a/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.hxx b/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.hxx
new file mode 100644
index 0000000000000000000000000000000000000000..0c35e1ad6700064f9edfdec2de34dcf28bad44b8
--- /dev/null
+++ b/Modules/Filtering/ImageManipulation/include/otbResetMarginFilter.hxx
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbResetMarginFilter_hxx
+#define otbResetMarginFilter_hxx
+
+#include "otbResetMarginFilter.h"
+#include "otbInterval.h"
+#include "otbMacro.h"
+#include "otbLogHelpers.h"
+#include "itkImageScanlineConstIterator.h"
+#include "itkImageScanlineIterator.h"
+#include "itkProgressReporter.h"
+#include <boost/numeric/interval.hpp>
+#include <algorithm>
+#include <cassert>
+#include <ostream>
+
+template <typename T, typename P>
+inline
+std::ostream & operator<<(std::ostream & os, boost::numeric::interval<T,P> const& v)
+{
+  return os << '[' << v.lower() << ".." << v.upper() << '[';
+}
+
+namespace otb
+{
+
+template<typename TImage>
+void
+ResetMarginFilter<TImage>
+::ThreadedGenerateData(
+    OutputImageRegionType const& outputRegionForThread,
+    itk::ThreadIdType            threadId)
+{
+  // otbMsgDevMacro("ThreadedGenerateData begin("<<NeatRegionLogger{outputRegionForThread}<<")");
+  using InputIterator   = itk::ImageScanlineConstIterator<InputImageType const>;
+  using OutputIterator  = itk::ImageScanlineIterator<OutputImageType>;
+
+  auto const* input  = this->GetInput();
+  auto      * output = this->GetOutput();
+  assert(input);
+  assert(output);
+  InputIterator  inputIterator (input,  OutputRegionToInputRegion(outputRegionForThread));
+  OutputIterator outputIterator(output, outputRegionForThread);
+
+  auto const& imgRegion = output->GetLargestPossibleRegion();
+  auto const  imgSizeX  = imgRegion.GetSize()[0];
+  auto const  imgSizeY  = imgRegion.GetSize()[1];
+  itk::IndexValueType const  imgEndX   = imgRegion.GetIndex()[0] + imgSizeX;
+  itk::IndexValueType const  imgEndY   = imgRegion.GetIndex()[1] + imgSizeY;
+
+  auto const& size      = outputRegionForThread.GetSize();
+  auto const& index     = outputRegionForThread.GetIndex();
+  auto const  sizeX     = size[0];
+  auto const  sizeY     = size[1];
+  auto const  startX    = index[0];
+  auto const  startY    = index[1];
+  itk::IndexValueType const  endX      = startX + sizeX;
+  itk::IndexValueType const  endY      = startY + sizeY;
+  auto const  thrX1     = std::min<itk::IndexValueType>(endX, m_thresholdX);
+  auto const  thrX2     = std::min<itk::IndexValueType>(endX, imgEndX - m_thresholdX);
+  auto const  thrY1     = std::min<itk::IndexValueType>(endY, m_thresholdYtop);
+  auto const  thrY2     = std::min<itk::IndexValueType>(endY, imgEndY - m_thresholdYbot);
+
+  assert(thrX1 <= endX && "Iterations shall stay within requested region");
+  assert(thrX2 <= endX && "Iterations shall stay within requested region");
+  assert(thrY1 <= endY && "Iterations shall stay within requested region");
+  assert(thrY2 <= endY && "Iterations shall stay within requested region");
+
+  // using interval_t = boost::numeric::interval<long>;
+  using interval_t = Interval;
+  auto const region      = interval_t{startX, endX};
+  auto const zero_left   = intersect(interval_t{startX, thrX1}, region);
+  auto const copy_middle = intersect(interval_t{thrX1, thrX2},  region);
+  auto const zero_right  = intersect(interval_t{thrX2, endX},   region);
+  otbMsgDevMacro("X in " << zero_left   << " <<-- 0");
+  otbMsgDevMacro("X in " << copy_middle << " <<-- copy input");
+  otbMsgDevMacro("X in " << zero_right  << " <<-- 0");
+  otbMsgDevMacro("Y in ["<<startY<<".."<<thrY1<<"[  <<--- 0");
+
+  auto const nb_z_l = zero_left.upper()   - zero_left.lower();
+  auto const nb_c_m = copy_middle.upper() - copy_middle.lower();
+  auto const nb_z_r = zero_right.upper()  - zero_right.lower();
+  assert(nb_z_l + nb_c_m + nb_z_r == sizeX);
+
+  itk::ProgressReporter progress( this, threadId, outputRegionForThread.GetNumberOfPixels() / sizeY );
+  outputIterator.GoToBegin();
+
+  // TODO: Can we consider that lines are contiguous in memory?
+  // If so, we could have only 2 fill_n and one copy_n!
+  auto y = startY;
+  for (
+      ; y < thrY1
+      ; ++y, outputIterator.NextLine())
+  {
+    // If there is any trimming of first lines, the inputIterator iterator will
+    // directly point to the right region. we shall not increment it!
+    // otbMsgDevMacro("o(" << y << ") <-- 0");
+    assert(! outputIterator.IsAtEnd());
+    outputIterator.GoToBeginOfLine();
+    std::fill_n(&outputIterator.Value(), sizeX, OutputPixelType{});
+    progress.CompletedPixel(); // Completed...Line()
+  }
+  assert(y == thrY1 || y == startY);
+  otbMsgDevMacro("Y in ["<<thrY1<<".."<<thrY2<<"[  <<--- Input");
+  inputIterator.GoToBegin();
+  for (
+      ; y < thrY2
+      ; ++y, inputIterator.NextLine(), outputIterator.NextLine())
+  {
+    // otbMsgDevMacro("o(" << y << ") <-- Input");
+    assert(! inputIterator.IsAtEnd());
+    assert(! outputIterator.IsAtEnd());
+    inputIterator.GoToBeginOfLine();
+    outputIterator.GoToBeginOfLine();
+    auto const t1 = std::fill_n(&outputIterator.Value(), nb_z_l, OutputPixelType{});
+    // If there is any trimming of first columns, the inputIterator iterator
+    // will directly point to the right region. we shall not apply an offset!
+    auto const t2 = std::copy_n(&inputIterator.Value(), nb_c_m, t1);
+    std::fill_n(t2, nb_z_r, OutputPixelType{});
+    progress.CompletedPixel(); // Completed...Line()
+  }
+  assert(y == thrY2 || y == startY);
+  otbMsgDevMacro("Y in ["<<thrY2<<".."<<endY<<"[  <<--- 0");
+  for (
+      ; y < endY
+      ; ++y, outputIterator.NextLine())
+  {
+    // If there is any trimming of last lines, the inputIterator iterator will
+    // directly point to the right region. we shall not increment it!
+    // otbMsgDevMacro("o(" << y << ") <-- 0");
+    assert(! outputIterator.IsAtEnd());
+    outputIterator.GoToBeginOfLine();
+    std::fill_n(&outputIterator.Value(), sizeX, OutputPixelType{});
+    progress.CompletedPixel(); // Completed...Line()
+  }
+  assert(y == endY);
+  otbMsgDevMacro("ThreadedGenerateData end");
+}
+
+template<typename TImage>
+typename ResetMarginFilter<TImage>::InputImageRegionType
+ResetMarginFilter<TImage>
+::OutputRegionToInputRegion(OutputImageRegionType const& srcRegion)
+{
+  auto const* output = this->GetOutput();
+  assert(output);
+
+  auto const& maxRegion = output->GetLargestPossibleRegion();
+  auto const& maxSize   = maxRegion.GetSize();
+  auto const& maxStart  = maxRegion.GetIndex();
+
+  auto const& reqRegion = srcRegion;
+  auto const& reqSize   = reqRegion.GetSize();
+  auto const& reqStart  = reqRegion.GetIndex();
+
+  // using interval_t = boost::numeric::interval<long>;
+  using interval_t = Interval;
+  auto const maxRegionX = interval_t{
+    maxStart[0]+m_thresholdX,
+    static_cast<itk::IndexValueType>(maxStart[0]+maxSize[0]-m_thresholdX)
+  };
+  auto const maxRegionY = interval_t{
+    maxStart[1]+m_thresholdYtop,
+    static_cast<itk::IndexValueType>(maxStart[1]+maxSize[1]-m_thresholdYbot)
+  };
+
+  auto const reqRegionX = interval_t::OfLength(reqStart[0], reqSize[0]);
+  auto const reqRegionY = interval_t::OfLength(reqStart[1], reqSize[1]);
+#if 0
+  otbMsgDevMacro("OutputRegionToInputRegion: "
+      << "out="<< NeatRegionLogger{reqRegion}
+      << ";    max: x="<<maxRegionX << "  y="<<maxRegionY
+      << ";    req: x="<<reqRegionX << "  y="<<reqRegionY
+      );
+#endif
+
+  auto const inRegionX = intersect(reqRegionX, maxRegionX);
+  auto const inRegionY = intersect(reqRegionY, maxRegionY);
+  // otbMsgDevMacro(" --> ∩X: " << inRegionX << " ∩Y: " << inRegionY);
+
+  const InputIndexType inStart{inRegionX.lower(), inRegionY.lower()};
+  assert(inRegionX.lower() <= inRegionX.upper());
+  assert(inRegionY.lower() <= inRegionY.upper());
+  const InputSizeType inSize{
+    static_cast<unsigned long>(inRegionX.upper()-inRegionX.lower()),
+    static_cast<unsigned long>(inRegionY.upper()-inRegionY.lower())
+  };
+  auto const inRegion = InputImageRegionType{inStart, inSize};
+  otbMsgDevMacro("OutputRegionToInputRegion: out="<< NeatRegionLogger{reqRegion}<<"   --> in="<<NeatRegionLogger{inRegion});
+  return inRegion;
+}
+
+} // otb namespace
+
+#endif  // otbResetMarginFilter_hxx
+
diff --git a/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h b/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h
index 5e52e0b4a23d9b0e3d61bc633aaf78606c0901ab..a45546995001f51ab0a0a38409b27283644eb666 100644
--- a/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h
+++ b/Modules/Filtering/ImageManipulation/include/otbSpectralInformationDivergenceFunctor.h
@@ -25,6 +25,7 @@
 #include <algorithm>
 #include <vector>
 #include <numeric>
+#include <stdexcept>
 
 namespace otb
 {
@@ -37,7 +38,7 @@ namespace Functor
  *  Du, Yingzi & Chang, Chein-I & Ren, Hsuan & Chang, Chein-Chi & Jensen, James & D'Amico, Francis. (2004). "
       "New Hyperspectral Discrimination Measure for Spectral Characterization. Optical Engineering - OPT ENG. 43."
       " 1777-1786. 10.1117/1.1766301.
- * 
+ *
  * \ingroup OTBImageManipulation
  */
 template <class TInput, class TReference, class TOutput>
@@ -46,17 +47,17 @@ class SpectralInformationDivergenceFunctor
 public:
   SpectralInformationDivergenceFunctor() = default;
   virtual ~SpectralInformationDivergenceFunctor() = default;
-  
+
   using OutputValueType = typename TOutput::ValueType;
-  
+
   // Binary operator
   inline TOutput operator()(const TInput& input) const
   {
     TOutput res;
     res.SetSize(m_ReferenceProbabilities.size());
-    
+
     auto inputProbability = ComputeProbabilityMassFunction(input);
-    
+
     for (unsigned int i = 0; i< m_ReferenceProbabilities.size(); i++)
     {
       res[i] = ComputeSpectralInformationDivergence(inputProbability, m_ReferenceProbabilities[i]);
@@ -80,19 +81,19 @@ public:
       m_ReferenceProbabilities.push_back(ComputeProbabilityMassFunction(pixel));
     }
   }
-  
+
 private:
   inline TInput ComputeProbabilityMassFunction(TInput const & input) const
   {
     for (unsigned int i = 0; i < input.Size(); i++)
     {
       // Input pixel should be non negative (e.g. reflectance, radiance)
-      if (input[i] <= 0) 
+      if (input[i] <= 0)
       {
         throw std::runtime_error("Input pixels of the spectral information divergence algorithm should be strictly positive.");
       }
     }
-    
+
     return input / std::accumulate(&input[0], &input[input.Size()], 0.0);
   }
 
@@ -108,7 +109,7 @@ private:
     return sid;
   }
 
-  /** Probability mass function associated with the reference pixel */ 
+  /** Probability mass function associated with the reference pixel */
   std::vector<TReference> m_ReferenceProbabilities;
 };
 
diff --git a/Modules/Filtering/ImageManipulation/otb-module.cmake b/Modules/Filtering/ImageManipulation/otb-module.cmake
index 06d3a62ac64327463c4d2b6729b4c4cbc5835bcf..82fae997aad1b336e04e7155b58ed95c7284da90 100644
--- a/Modules/Filtering/ImageManipulation/otb-module.cmake
+++ b/Modules/Filtering/ImageManipulation/otb-module.cmake
@@ -36,6 +36,7 @@ otb_module(OTBImageManipulation
     OTBObjectList
     OTBStreaming
     OTBTransform
+    OTBIOGDAL
 
   TEST_DEPENDS
     OTBDensity
diff --git a/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.h b/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.h
index 9b5f3fccce69ced430c573438cb89ae9e85a31b8..eb167b2fa46b3ae22e88c0c5b86da44e3dff7e6a 100644
--- a/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.h
+++ b/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.h
@@ -98,7 +98,6 @@ public:
 
   /** DEM typedef */
   typedef otb::DEMHandler                  DEMHandlerType;
-  typedef typename DEMHandlerType::Pointer DEMHandlerPointerType;
 
   /** Method for creation through the object factory. */
   itkNewMacro(Self);
@@ -125,10 +124,6 @@ public:
   itkSetMacro(MeanElevation, double);
   itkGetConstReferenceMacro(MeanElevation, double);
 
-  /** Set/Get the DEMHandler */
-  itkSetObjectMacro(DEMHandler, DEMHandlerType);
-  itkGetObjectMacro(DEMHandler, DEMHandlerType);
-
   /** Get the residual ground error */
   itkGetConstReferenceMacro(RMSGroundError, double);
 
@@ -207,9 +202,6 @@ private:
    * over the image is used instead */
   double m_MeanElevation;
 
-  /** The DEMHandler */
-  DEMHandlerPointerType m_DEMHandler;
-
   /** Container of GCPs */
   GCPsContainerType m_GCPsContainer;
 
diff --git a/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.hxx b/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.hxx
index c13ebc85e51fe7982e37970548f990d6afef56ba..a3df285e01aa01b83f4029c7b1a3690c9abb9204 100644
--- a/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.hxx
+++ b/Modules/Filtering/Projection/include/otbGCPsToRPCSensorModelImageFilter.hxx
@@ -40,7 +40,6 @@ GCPsToRPCSensorModelImageFilter<TImage>::GCPsToRPCSensorModelImageFilter()
     m_MeanError(0.),
     m_UseDEM(false),
     m_MeanElevation(0.),
-    m_DEMHandler(),
     m_GCPsContainer(),
     m_ModelUpToDate(false)
 {
@@ -51,9 +50,6 @@ GCPsToRPCSensorModelImageFilter<TImage>::GCPsToRPCSensorModelImageFilter()
 
   // Clear the GCPs container
   this->ClearGCPs();
-
-  /** Create the DEM handler */
-  m_DEMHandler = DEMHandler::Instance();
 }
 
 template <class TImage>
@@ -122,7 +118,7 @@ void GCPsToRPCSensorModelImageFilter<TImage>::AddGCP(const Point2DType& sensorPo
   if (m_UseDEM)
   {
     // If so, use it to get the elevation
-    double height = m_DEMHandler->GetHeightAboveEllipsoid(groundPoint);
+    double height = DEMHandler::GetInstance().GetHeightAboveEllipsoid(groundPoint);
     // To avoid nan value
     if (height != height)
       height                    = 0;
diff --git a/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.h b/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.h
index 0fd11cdabe94a3215ca5b475f70cbf9ade550c0c..0bdf9b11d514bba30129dfca86b3cafa0b58acdd 100644
--- a/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.h
+++ b/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.h
@@ -141,7 +141,6 @@ public:
 
   /**
    * Set/Get input & output projections.
-   * Set/Get input & output keywordlist
    * The macro are not used here cause the input and the output are
    * inversed.
    */
@@ -167,7 +166,9 @@ public:
     return m_Transform->GetInputProjectionRef();
   }
 
-  /** Set/Get Input Keywordlist*/
+  /** Set/Get Input Keywordlist
+   * \deprecated
+   */
   void SetInputKeywordList(const ImageKeywordlist& kwl)
   {
     m_Transform->SetOutputKeywordList(kwl);
@@ -178,7 +179,9 @@ public:
     return m_Transform->GetOutputKeywordList();
   }
 
-  /** Set/Get output Keywordlist*/
+  /** Set/Get output Keywordlist
+   * \deprecated
+   */
   void SetOutputKeywordList(const ImageKeywordlist& kwl)
   {
     m_Transform->SetInputKeywordList(kwl);
diff --git a/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.hxx b/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.hxx
index 6f971ebc42c8e69551248ff1738d4ab6520eef7d..9706a6754f134777873d8d3ac82d16e2da19777f 100644
--- a/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.hxx
+++ b/Modules/Filtering/Projection/include/otbGenericRSResampleImageFilter.hxx
@@ -107,6 +107,8 @@ void GenericRSResampleImageFilter<TInputImage, TOutputImage>::GenerateOutputInfo
   {
     itk::EncapsulateMetaData<ImageKeywordlist>(dict, MetaDataKey::OSSIMKeywordlistKey, this->GetOutputKeywordList());
   }
+
+  this->GetOutput()->SetProjectionRef(this->GetOutputProjectionRef());
 }
 
 /**
diff --git a/Modules/Filtering/Projection/test/otbGCPsToRPCSensorModelImageFilterCheckRpcModel.cxx b/Modules/Filtering/Projection/test/otbGCPsToRPCSensorModelImageFilterCheckRpcModel.cxx
index f9eec4089a28b97b08b3a9188b7db8c8a63ac86c..0370201ff9b03d23497679bc119d902a4c63e39a 100644
--- a/Modules/Filtering/Projection/test/otbGCPsToRPCSensorModelImageFilterCheckRpcModel.cxx
+++ b/Modules/Filtering/Projection/test/otbGCPsToRPCSensorModelImageFilterCheckRpcModel.cxx
@@ -96,7 +96,7 @@ int otbGCPsToRPCSensorModelImageFilterCheckRpcModel(int argc, char* argv[])
 
   // Set the DEM Directory
   if (std::string(argv[2]).compare("no_output") != 0)
-    otb::DEMHandler::Instance()->OpenDEMDirectory(argv[2]);
+    otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[2]);
 
   grsTrasnform->InstantiateTransform();
 
diff --git a/Modules/Filtering/Projection/test/otbGenericRSResampleImageFilter.cxx b/Modules/Filtering/Projection/test/otbGenericRSResampleImageFilter.cxx
index 13fbd123fc4f37b83940f13096a6cea1a59a8289..0659a95767ff8916db1349abc9805387bba55b29 100644
--- a/Modules/Filtering/Projection/test/otbGenericRSResampleImageFilter.cxx
+++ b/Modules/Filtering/Projection/test/otbGenericRSResampleImageFilter.cxx
@@ -30,6 +30,7 @@
 #include "itkUnaryFunctorImageFilter.h"
 
 #include "otbDEMHandler.h"
+
 #include "otbUnaryImageFunctorWithVectorImageFilter.h"
 #include "otbGenericRSResampleImageFilter.h"
 #include "otbComplexToIntensityImageFilter.h"
@@ -113,10 +114,9 @@ int otbGenericRSResampleImageFilter(int argc, char* argv[])
   origin[1] = strtod(argv[4], nullptr); // Origin northing
   orthoRectifFilter->SetOutputOrigin(origin);
 
-  std::string wkt =
-      otb::SpatialReference::FromUTM(atoi(argv[9]), atoi(argv[10]) ? otb::SpatialReference::hemisphere::north : otb::SpatialReference::hemisphere::south)
-          .ToWkt();
-  orthoRectifFilter->SetOutputProjectionRef(wkt);
+  auto inputSpatialRef = otb::SpatialReference::FromUTM(atoi(argv[9]), atoi(argv[10]) ? otb::SpatialReference::hemisphere::north : otb::SpatialReference::hemisphere::south);
+
+  orthoRectifFilter->SetOutputProjectionRef(inputSpatialRef.ToWkt());
 
   // Displacement Field spacing
   VectorImageType::SpacingType gridSpacing;
@@ -127,16 +127,26 @@ int otbGenericRSResampleImageFilter(int argc, char* argv[])
   // manage demHandler
   if (atoi(argv[12]) == 1) // mode = no DEM
   {
-    otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(135.8);
+    otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(135.8);
   }
   else if ((atoi(argv[12]) == 2) || (atoi(argv[12]) == 3)) // mode = DEM SRTM || DEM GTIFF
   {
-    otb::DEMHandler::Instance()->OpenDEMDirectory(argv[13]);
+    otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[13]);
   }
 
   writer->SetInput(orthoRectifFilter->GetOutput());
   writer->SetNumberOfDivisionsTiledStreaming(4);
   writer->Update();
 
+  auto outputProjectionRef = orthoRectifFilter->GetOutput()->GetProjectionRef();
+  if (outputProjectionRef.empty() ||
+    otb::SpatialReference::FromDescription(outputProjectionRef) != inputSpatialRef)
+  {
+    std::cout << "Input and output projection don't match. "
+              << "The output projection is: "
+              << outputProjectionRef
+              << std::endl;
+    return EXIT_FAILURE;
+  }
   return EXIT_SUCCESS;
 }
diff --git a/Modules/Filtering/Projection/test/otbGenericRSTransformFromImage.cxx b/Modules/Filtering/Projection/test/otbGenericRSTransformFromImage.cxx
index 4a8af39b5bae329823500f5e48f3025341664b7f..1410fceaf98001ca50b7768e91896a1fef7dd49d 100644
--- a/Modules/Filtering/Projection/test/otbGenericRSTransformFromImage.cxx
+++ b/Modules/Filtering/Projection/test/otbGenericRSTransformFromImage.cxx
@@ -117,10 +117,10 @@ int otbGenericRSTransformImageAndMNTToWGS84ConversionChecking(int itkNotUsed(arg
   GeoDistanceType::Pointer   geoDistance   = GeoDistanceType::New();
   GeoDistance3DType::Pointer geoDistance3d = GeoDistance3DType::New();
 
-  otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
-  demHandler->OpenDEMDirectory(argv[2]);
-  demHandler->OpenGeoidFile(argv[3]);
-  double heightAboveEllipsoid = demHandler->GetHeightAboveEllipsoid(refGeoPt);
+  auto & demHandler = otb::DEMHandler::GetInstance();
+  demHandler.OpenDEMDirectory(argv[2]);
+  demHandler.OpenGeoidFile(argv[3]);
+  double heightAboveEllipsoid = demHandler.GetHeightAboveEllipsoid(refGeoPt);
 
   // Instantiate WGS->Image transform
   TransformType::Pointer wgs2img = TransformType::New();
diff --git a/Modules/Filtering/Projection/test/otbGenericRSTransformGenericTest.cxx b/Modules/Filtering/Projection/test/otbGenericRSTransformGenericTest.cxx
index 9f01ebb03aa828e38f604aeaeb2f179e3eb36651..cbc42748a502c25d1465a6c312e0f332ce146379 100644
--- a/Modules/Filtering/Projection/test/otbGenericRSTransformGenericTest.cxx
+++ b/Modules/Filtering/Projection/test/otbGenericRSTransformGenericTest.cxx
@@ -25,6 +25,7 @@
 #include "itkEuclideanDistanceMetric.h"
 #include "otbSpatialReference.h"
 #include "otbGeographicalDistance.h"
+#include "otbDEMHandler.h"
 
 typedef otb::Image<unsigned short>                          ImageType;
 typedef otb::ImageFileReader<ImageType>                     ReaderType;
@@ -150,7 +151,7 @@ int otbGenericRSTransformGenericTest(int argc, char* argv[])
     }
 
     double averageElevation = atof(argv[14]);
-    otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(averageElevation);
+    otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(averageElevation);
 
     std::cout << "Average elevation " << averageElevation << " used." << std::endl;
   }
@@ -162,7 +163,7 @@ int otbGenericRSTransformGenericTest(int argc, char* argv[])
       return EXIT_FAILURE;
     }
 
-    otb::DEMHandler::Instance()->OpenDEMDirectory(argv[14]);
+    otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[14]);
 
     std::cout << "Elevation from DEM " << argv[14] << " used." << std::endl;
   }
diff --git a/Modules/Filtering/Projection/test/otbSensorModel.cxx b/Modules/Filtering/Projection/test/otbSensorModel.cxx
index 208d57357442d061c1094d9fde818a64e04c9f76..baf10d54e862cb4cee6ef2de01dc942562c9ac0f 100644
--- a/Modules/Filtering/Projection/test/otbSensorModel.cxx
+++ b/Modules/Filtering/Projection/test/otbSensorModel.cxx
@@ -72,7 +72,7 @@ int produceGCP(char* outputgcpfilename, const otb::ImageKeywordlist& kwlist, boo
     return EXIT_FAILURE;
   }
 
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(z);
+  otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(z);
 
   // ossim classes
   ossimKeywordlist ossimKwlist;
@@ -347,7 +347,7 @@ int otbSensorModel(int argc, char* argv[])
     geo3dPoint = *geo3dPointsIt;
 
     double z = geo3dPoint[2];
-    otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(z);
+    otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(z);
 
     // otbForwardSensorModel and otbInverseSensorModel
     geoPoint           = forwardSensorModel->TransformPoint(imagePoint);
diff --git a/Modules/Filtering/Projection/test/otbTileImageFilterRSTransformTest.cxx b/Modules/Filtering/Projection/test/otbTileImageFilterRSTransformTest.cxx
index 24ece3588e42a48cfc7eabf63ea734941ca5381d..c91665f6a4f784925b2c419c1d032fd5af168cf8 100644
--- a/Modules/Filtering/Projection/test/otbTileImageFilterRSTransformTest.cxx
+++ b/Modules/Filtering/Projection/test/otbTileImageFilterRSTransformTest.cxx
@@ -24,6 +24,7 @@
 #include "otbGeographicalDistance.h"
 #include "otbGenericRSTransform.h"
 #include <iomanip>
+#include "otbDEMHandler.h"
 
 typedef otb::Image<unsigned int>             ImageType;
 typedef otb::TileImageFilter<ImageType>      TileImageFilterType;
@@ -49,7 +50,7 @@ int otbTileImageFilterRSTransformTest(int argc, char* argv[])
   TileImageFilterType::Pointer tileFilter = TileImageFilterType::New();
   tileFilter->SetLayout(layout);
 
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(0);
+  otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(0);
 
   for (unsigned int i = 0; i < numberOfImages; ++i)
   {
diff --git a/Modules/Filtering/Projection/test/otbVectorDataIntoImageProjectionFilterTest.cxx b/Modules/Filtering/Projection/test/otbVectorDataIntoImageProjectionFilterTest.cxx
index dd1aec17c81ca56118ca4652bf8fc07a22f93681..36a3335abfa0a62920c2b7035a47a9658de33c49 100644
--- a/Modules/Filtering/Projection/test/otbVectorDataIntoImageProjectionFilterTest.cxx
+++ b/Modules/Filtering/Projection/test/otbVectorDataIntoImageProjectionFilterTest.cxx
@@ -131,7 +131,7 @@ int otbVectorDataIntoImageProjectionFilterCompareImplTest(int itkNotUsed(argc),
 
   if (!demDirectory.empty())
   {
-    otb::DEMHandler::Instance()->OpenDEMDirectory(demDirectory);
+    otb::DEMHandler::GetInstance().OpenDEMDirectory(demDirectory);
   }
 
   // Read the image
diff --git a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx
index a7f35d2830be205ba91436c9d507a06d502880b8..364f587dc1b972df4a54e7bdbfb913bb1e71f6d9 100644
--- a/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx
+++ b/Modules/IO/ExtendedFilename/src/otbExtendedFilenameToWriterOptions.cxx
@@ -225,7 +225,7 @@ void ExtendedFilenameToWriterOptions::SetExtendedFileName(const std::string& ext
 	{
 	  code = std::stoi(map["epsg"]);
 	}
-	catch(const std::invalid_argument& e)
+	catch(const std::invalid_argument&)
 	{
 	  itkWarningMacro("Invalid value ("
                       << map["epsg"]
diff --git a/Modules/IO/IOGDAL/include/otbDEMHandler.h b/Modules/IO/IOGDAL/include/otbDEMHandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..830a5a90d009dbd0a38bad8118bb63c3270b758e
--- /dev/null
+++ b/Modules/IO/IOGDAL/include/otbDEMHandler.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef otbDEMHandler_h
+#define otbDEMHandler_h
+
+#include "otbImage.h"
+#include "otbGDALDriverManagerWrapper.h"
+
+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
+ * - SRTM available, but no geoid: srtm_value
+ * - No SRTM and no geoid available: default height above ellipsoid
+ *
+ * GetHeightAboveMSL():
+ * - 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
+ *
+ * \ingroup OTBIOGDAL
+ */
+class DEMHandler
+{
+public:
+  using Self =          DEMHandler;
+  using PointType =     itk::Point<double, 2>;
+
+  /** Retrieve the singleton instance */
+  static DEMHandler & GetInstance();
+
+  /** Try to open the DEM directory. 
+  * \param path input path
+  */
+  void OpenDEMFile(const std::string & path);
+
+  /** Open all raster in the directory. 
+  * \param DEMDirectory input directory
+  */
+  void OpenDEMDirectory(const std::string& DEMDirectory);
+
+  /** Tells whether the directory contains a raster
+  * \param DEMDirectory input directory
+  */
+  bool IsValidDEMDirectory(const std::string& DEMDirectory) const;
+
+  /** 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) const;
+
+  double GetHeightAboveEllipsoid(const PointType& geoPoint) const;
+ 
+  /** 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) const;
+
+  double GetHeightAboveMSL(const PointType& geoPoint) const;
+
+  /** Return the number of DEM opened */
+  unsigned int GetDEMCount() const;
+  
+  double GetDefaultHeightAboveEllipsoid() const;
+
+  void SetDefaultHeightAboveEllipsoid(double height);
+
+  /** 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();
+
+
+private:
+  DEMHandler(const Self&) = delete;
+  void operator=(const Self&) = delete;
+  /** List of RAII capsules on all opened DEM datasets for memory management */
+  std::vector<otb::GDALDatasetWrapper::Pointer> m_DatasetList;
+  
+  /** Pointer to the DEM dataset */
+  GDALDataset * m_Dataset;
+
+  /** Pointer to the geoid dataset */
+  GDALDataset* m_GeoidDS;
+  
+  /** Default height above elliposid, used when no DEM or geoid height is available. */
+  double m_DefaultHeightAboveEllipsoid;
+
+  /** List of the DEM directories currently opened */
+  std::vector<std::string> m_DEMDirectories;
+
+  /** Filename of the current geoid */
+  std::string m_GeoidFilename;
+
+};
+
+}
+#endif
diff --git a/Modules/IO/IOGDAL/src/CMakeLists.txt b/Modules/IO/IOGDAL/src/CMakeLists.txt
index fe13c4c0416aae3612dfae1c904fbbcc625a2f52..4ccae4de9d36eb02393e6772017593ac7a9a14a9 100644
--- a/Modules/IO/IOGDAL/src/CMakeLists.txt
+++ b/Modules/IO/IOGDAL/src/CMakeLists.txt
@@ -27,6 +27,7 @@ set(OTBIOGDAL_SRC
   otbOGRIOHelper.cxx
   otbOGRVectorDataIO.cxx
   otbOGRVectorDataIOFactory.cxx
+  otbDEMHandler.cxx
   otbGDALImageMetadataInterface.cxx
   )
 
@@ -40,6 +41,7 @@ target_link_libraries(OTBIOGDAL
   ${OTBGDAL_LIBRARIES}
   ${OTBBoost_LIBRARIES}
   ${OTBOSSIMAdapters_LIBRARIES}
+   ${Boost_LIBRARIES}
   )
 
 otb_module_target(OTBIOGDAL)
diff --git a/Modules/IO/IOGDAL/src/otbDEMHandler.cxx b/Modules/IO/IOGDAL/src/otbDEMHandler.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ee175b97dd2e020f306f90de8146d10f59f8c829
--- /dev/null
+++ b/Modules/IO/IOGDAL/src/otbDEMHandler.cxx
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "otbDEMHandler.h"
+#include "otbGDALDriverManagerWrapper.h"
+#include "boost/filesystem.hpp"
+#include <boost/range/iterator_range.hpp>
+#include "gdal_utils.h"
+
+//TODO C++ 17 : use std::optional instead
+#include <boost/optional.hpp>
+
+// TODO : RemoveOSSIM
+#include <otbOssimDEMHandler.h>
+
+#include <mutex>
+
+#include "ogr_spatialref.h"
+
+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)
+{
+  std::vector<std::string> fileList;
+  
+  if ( !boost::filesystem::exists( directoryPath) )
+  {
+    return fileList;
+  }
+  else if (!boost::filesystem::is_directory(directoryPath))
+  {
+    fileList.push_back(directoryPath);
+    return fileList;
+  }
+  
+  // End iterator : default construction yields past-the-end
+  for ( const auto & item : boost::make_iterator_range(boost::filesystem::directory_iterator(directoryPath), {}) )
+  {
+    if ( boost::filesystem::is_directory(item.status()) )
+    {
+      auto subDirList = GetFilesInDirectory(item.path().string());
+      fileList.insert(fileList.end(), subDirList.begin(), subDirList.end());
+    }
+    else
+    {
+      fileList.push_back(item.path().string());
+    }
+  }
+
+  return fileList;
+}
+
+std::mutex demMutex;
+
+boost::optional<double> GetDEMValue(double lon, double lat, GDALDataset& ds)
+{
+  const std::lock_guard<std::mutex> lock(demMutex);
+
+#if GDAL_VERSION_NUM >= 3000000
+  auto srs = ds.GetSpatialRef();
+#else
+  auto projRef = ds.GetProjectionRef();
+  
+  std::unique_ptr<OGRSpatialReference> srsUniquePtr;
+  OGRSpatialReference* srs = nullptr;
+  // GetProjectionRef() returns an empty non null string if no projection is available
+  if (strlen(projRef) != 0 )
+  {
+    srsUniquePtr = std::make_unique<OGRSpatialReference>(ds.GetProjectionRef());
+    srs = srsUniquePtr.get();
+  }
+#endif
+
+  auto wgs84Srs = OGRSpatialReference::GetWGS84SRS();
+
+  // Convert input lon lat into the coordinates defined by the dataset if needed.
+  if (srs && !srs->IsSame(wgs84Srs))
+  {
+    auto poCT = std::unique_ptr<OGRCoordinateTransformation>
+                    (OGRCreateCoordinateTransformation(wgs84Srs, srs));
+
+    if (poCT && !poCT->Transform( 1, &lon, &lat ) )
+    {
+      return boost::none;
+    }
+  }
+
+  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
+
+// Meyer singleton design pattern
+DEMHandler & DEMHandler::GetInstance() 
+{
+  static DEMHandler s_instance;
+  return s_instance;
+}
+
+DEMHandler::DEMHandler() : m_Dataset(nullptr),
+                           m_GeoidDS(nullptr),
+                           m_DefaultHeightAboveEllipsoid(0.0)
+{
+  GDALAllRegister();
+};
+
+DEMHandler::~DEMHandler()
+{
+  if (m_GeoidDS)
+  {
+    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);
+}
+
+void DEMHandler::OpenDEMDirectory(const std::string& DEMDirectory)
+{  
+  // TODO : RemoveOSSIM
+  OssimDEMHandler::Instance()->OpenDEMDirectory(DEMDirectory);
+
+  auto demFiles = DEMDetails::GetFilesInDirectory(DEMDirectory);
+  for (const auto & file : demFiles)
+  {
+    auto ds = otb::GDALDriverManagerWrapper::GetInstance().Open(file);
+    if (ds)
+    {
+      m_DatasetList.push_back(ds );
+    }
+  }
+  
+  int vrtSize = m_DatasetList.size();
+ 
+  // Don't build a vrt if there is less than two dataset
+  if (m_DatasetList.empty())
+  {
+    m_Dataset = nullptr;
+  }
+  else
+  {
+    if (vrtSize == 1)
+    {
+      m_Dataset = m_DatasetList[0]->GetDataSet();
+      m_DEMDirectories.push_back(DEMDirectory);
+    }
+    else
+    {
+      std::vector<GDALDatasetH> vrtDatasetList(vrtSize);
+      for (int i = 0; i < vrtSize; i++)
+      {
+        vrtDatasetList[i] = m_DatasetList[i]->GetDataSet();
+      }
+
+      m_Dataset = static_cast<GDALDataset *> (GDALBuildVRT(nullptr, vrtSize, vrtDatasetList.data(), 
+                                                nullptr, nullptr, nullptr));
+      m_DEMDirectories.push_back(DEMDirectory);
+    }
+    
+  }
+}
+
+bool DEMHandler::OpenGeoidFile(const std::string& geoidFile)
+{  
+  // TODO : RemoveOSSIM
+  OssimDEMHandler::Instance()->OpenGeoidFile(geoidFile);
+
+
+  int pbError;
+  auto ds = GDALOpenVerticalShiftGrid(geoidFile.c_str(), &pbError);
+  
+  // pbError is set to TRUE if an error happens.
+  if (pbError == 0)
+  {
+    if (m_GeoidDS)
+    {
+      GDALClose(m_GeoidDS);
+    }
+    m_GeoidDS = static_cast<GDALDataset*>(ds);
+    m_GeoidFilename = geoidFile;
+  }
+
+  return pbError;
+}
+
+double DEMHandler::GetHeightAboveEllipsoid(double lon, double lat) const
+{
+  double result = 0.;
+  boost::optional<double> DEMresult;
+  boost::optional<double> geoidResult;
+
+  if (m_Dataset)
+  {
+    DEMresult = DEMDetails::GetDEMValue(lon, lat, *m_Dataset);
+    if (DEMresult)
+    {
+      result += *DEMresult;
+    }
+  }
+
+
+  if (m_GeoidDS)
+  {
+    geoidResult = DEMDetails::GetDEMValue(lon, lat, *m_GeoidDS);
+    if (geoidResult)
+    {
+      result += *geoidResult;
+    }
+  }
+
+  if (DEMresult || geoidResult)
+    return result;
+  else
+    return m_DefaultHeightAboveEllipsoid;
+}
+
+double DEMHandler::GetHeightAboveEllipsoid(const PointType& geoPoint) const
+{
+  return GetHeightAboveEllipsoid(geoPoint[0], geoPoint[1]);
+}
+
+double DEMHandler::GetHeightAboveMSL(double lon, double lat) const
+{
+  if (m_Dataset)
+  { 
+    auto result = DEMDetails::GetDEMValue(lon, lat, *m_Dataset);
+    
+    if (result)
+    {
+      return *result;
+    }
+  }
+
+  return 0;
+}
+
+double DEMHandler::GetHeightAboveMSL(const PointType& geoPoint) const
+{
+  return GetHeightAboveMSL(geoPoint[0], geoPoint[1]);
+}
+
+unsigned int DEMHandler::GetDEMCount() const
+{
+  return m_DatasetList.size();
+}
+
+bool DEMHandler::IsValidDEMDirectory(const std::string& DEMDirectory) const
+{
+  for (const auto & filename : DEMDetails::GetFilesInDirectory(DEMDirectory))
+  {
+    // test if a driver can identify this dataset
+    auto identifyDriverH = GDALIdentifyDriver(filename.c_str(), nullptr);
+    if (identifyDriverH)
+    {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+std::string DEMHandler::GetDEMDirectory(unsigned int idx) const
+{
+  if (idx >= m_DEMDirectories.size())
+  {
+    std::ostringstream oss;
+    oss << "Requested DEM diectory " << idx
+        << ", but only "<< m_DEMDirectories.size() << "have been set.";
+    throw std::out_of_range (oss.str());
+  }
+  
+  return m_DEMDirectories[idx];
+  
+}
+
+std::string DEMHandler::GetGeoidFile() const
+{
+  return m_GeoidFilename;
+}
+
+void DEMHandler::ClearDEMs()
+{
+  // TODO : RemoveOSSIM
+  // This should be call, but this causes a segfault ... OssimDEMHandler will
+  // be removed in a near future anyway
+  // OssimDEMHandler::Instance()->ClearDEMs();
+  m_DEMDirectories.clear();
+
+  // This will call GDALClose on all datasets
+  m_DatasetList.clear();
+}
+
+void DEMHandler::SetDefaultHeightAboveEllipsoid(double height)
+{
+  OssimDEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(height);
+
+  m_DefaultHeightAboveEllipsoid = height;
+}
+
+double DEMHandler::GetDefaultHeightAboveEllipsoid() const
+{
+  return m_DefaultHeightAboveEllipsoid;
+}
+
+} // namespace otb
diff --git a/Modules/IO/ImageIO/test/CMakeLists.txt b/Modules/IO/ImageIO/test/CMakeLists.txt
index 3a8be81881a79c08c480e3608760e1c8a6b859e1..3bc5cbfe531fdd4e02ffcfba6405a64fae9f052a 100644
--- a/Modules/IO/ImageIO/test/CMakeLists.txt
+++ b/Modules/IO/ImageIO/test/CMakeLists.txt
@@ -1306,3 +1306,14 @@ otb_add_test(NAME ioTvCompoundMetadataReaderTest
   otbWriteGeomFile
   ${INPUTDATA}/QB_Toulouse_combo.vrt
   ${TEMP}/ioTvCompoundMetadataReaderTest.tif)
+
+
+
+
+otb_add_test(NAME ioTvCompareMetadataTest COMMAND otbImageIOTestDriver
+  --compare-metadata ${EPSILON_9}
+  LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
+  LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
+  otbImageFileReaderRGBTest
+  ${INPUTDATA}/couleurs_extrait.png
+  ${TEMP}/ioImageFileReaderRGB_PNG2PNG.png )
diff --git a/Modules/IO/KMZWriter/test/otbKmzProductWriter.cxx b/Modules/IO/KMZWriter/test/otbKmzProductWriter.cxx
index 9a42d77b4773cec62eb9922b1e2216b623fcb991..6cd218ec810afbad417752011f9845144eb8d96b 100644
--- a/Modules/IO/KMZWriter/test/otbKmzProductWriter.cxx
+++ b/Modules/IO/KMZWriter/test/otbKmzProductWriter.cxx
@@ -56,7 +56,7 @@ int otbKmzProductWriter(int argc, char* argv[])
   ReaderType::Pointer reader = ReaderType::New();
   reader->SetFileName(infname);
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(demPath);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(demPath);
 
   GCPsToSensorModelFilterType::Pointer rpcEstimator = GCPsToSensorModelFilterType::New();
   rpcEstimator->SetInput(reader->GetOutput());
@@ -164,7 +164,7 @@ int otbKmzProductWriterWithLogoAndLegend(int argc, char* argv[])
   kmzWriter->AddLegend(legendReader->GetOutput());
 
   // Set the DEM Directory
-  otb::DEMHandler::Instance()->OpenDEMDirectory(argv[4]);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[4]);
 
   // trigger the writing
   kmzWriter->Update();
diff --git a/Modules/IO/TestKernel/src/otbTestHelper.cxx b/Modules/IO/TestKernel/src/otbTestHelper.cxx
index 426db29ec55f9132f696067765210cddf108d657..fc00fe17d69512c41dd403199413caf2e0cb7720 100644
--- a/Modules/IO/TestKernel/src/otbTestHelper.cxx
+++ b/Modules/IO/TestKernel/src/otbTestHelper.cxx
@@ -1418,7 +1418,98 @@ int TestHelper::RegressionTestImage(int cpt, const char* testImageFilename, cons
   return ret;
 }
 
-int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char* baselineImageFilename, const double /*toleranceDiffPixelImage*/) const
+namespace
+{
+/** \fn CompareMetadataDict
+  \brief Compare two metadata dictionaries key by key. Dictionaries are assumed to be associative containers (e.g. std::map)
+  \param[in] baselineMap : reference metadata dictionary
+  \param[in] testMap : metadata dictionary to be compared
+  \param[in] reportErrors : print difference between dictionaries into srd::cerr
+  \param[in] untestedKeys : list of keys that should be ignored during comparison
+  \param[in] p bianry predicate used to compare elements (mapped type) of the two input maps
+  \return number of different elements.
+*/
+template <class MapType, class BinaryPredicate >
+int CompareMetadataDict( const MapType & baselineMap, 
+                         const MapType & testMap,
+                          bool reportErrors,
+                          std::vector< typename MapType::key_type> untestedKeys,
+                          const BinaryPredicate & p)
+{
+  auto first1 = testMap.begin();
+  auto last1 = testMap.end();
+  auto first2 = baselineMap.begin();
+  auto last2 = baselineMap.end();
+
+  if (std::distance(first1, last1) != std::distance(first2, last2))
+  {
+    if (reportErrors)
+    {
+      std::cerr << "Input metadata dictionaries have different sizes" << std::endl;
+    }
+
+    return 1;
+  }
+
+  int errorCount = 0;
+
+  while (first1 != last1)
+  {
+    if (std::find(untestedKeys.begin(), untestedKeys.end(), first1->first) == untestedKeys.end())
+    {
+      if (first1->first != first2->first)
+      {
+        errorCount++;
+        if (reportErrors)
+        {
+          std::cerr << "Metadata key " << otb::MetaData::EnumToString(first1->first) 
+                      << " does not match between test and baseline images: "
+                      << std::endl;
+        }
+        return errorCount;
+      }
+
+
+      if (!p(first1->second, first2->second))
+      {
+        errorCount++;
+        if (reportErrors)
+          std::cerr << "Metadata " << otb::MetaData::EnumToString(first1->first) 
+                    << " does not match between test and baseline images: "
+                    << std::endl
+                    << "Baseline image: " 
+                    << first1->second
+                    << std::endl
+                    << "Test image: " 
+                    << first2->second 
+                    << std::endl;
+      }
+    }
+    
+    ++first1;
+    ++first2;
+  }
+
+  return errorCount;
+}
+
+
+
+template <class MapType>
+int CompareMetadataDict( const MapType & baselineMap, 
+                         const MapType & testMap,
+                          bool reportErrors,
+                          std::vector< typename MapType::key_type> untestedKeys)
+{
+  auto p = []( const typename MapType::mapped_type & rhs, 
+               const typename MapType::mapped_type & lhs) 
+          {return rhs == lhs;};
+  return CompareMetadataDict(baselineMap, testMap, reportErrors, untestedKeys, p);
+}
+
+}
+
+int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char* baselineImageFilename, const double tolerance) const
 {
   // Use the factory mechanism to read the test and baseline files and convert them to double
   typedef otb::Image<double, ITK_TEST_DIMENSION_MAX> ImageType;
@@ -1449,6 +1540,7 @@ int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char
   }
 
   unsigned int errcount = 0;
+
   // The sizes of the baseline and test image must match
   ImageType::SizeType baselineSize;
   baselineSize = baselineReader->GetOutput()->GetLargestPossibleRegion().GetSize();
@@ -1583,12 +1675,84 @@ int TestHelper::RegressionTestMetaData(const char* testImageFilename, const char
       }
     }
   }
+  
+  const auto & baselineImageMetadata = blImPtr->GetImageMetadata();
+  const auto & testImageMetadata = testImPtr->GetImageMetadata();
+
+  // Compare string keys (strict equality)
+  errcount += CompareMetadataDict(baselineImageMetadata.StringKeys,
+                                    testImageMetadata.StringKeys,
+                                    m_ReportErrors,
+                                    {});
+
+  // Compare numeric keys
+  auto compareDouble = [tolerance](double lhs, double rhs)
+        {return fabs(lhs - rhs) 
+                <= ( (fabs(lhs) < fabs(rhs) ? fabs(rhs) : fabs(lhs)) * tolerance);};
+
+  // Don't test TileHints and datatype, as these metadata are written by gdal drivers, not otb.
+  std::vector<MDNum> untestedMDNum  = {MDNum::TileHintX, MDNum::TileHintY, MDNum::DataType};
+  errcount += CompareMetadataDict(baselineImageMetadata.NumericKeys,
+                                    testImageMetadata.NumericKeys,
+                                    m_ReportErrors,
+                                    untestedMDNum,
+                                    compareDouble);
+
+  // Compare time keys (strict equality)
+  errcount += CompareMetadataDict(baselineImageMetadata.TimeKeys, 
+                                    testImageMetadata.TimeKeys,
+                                    m_ReportErrors,
+                                    {});
+
+
+  // Compare LUTs (strict equality)
+  errcount += CompareMetadataDict(baselineImageMetadata.LUT1DKeys, 
+                                    testImageMetadata.LUT1DKeys,
+                                    m_ReportErrors,
+                                    {});
+
+  errcount += CompareMetadataDict(baselineImageMetadata.LUT2DKeys, 
+                                    testImageMetadata.LUT2DKeys,
+                                    m_ReportErrors,
+                                    {});
+
+
+  // Compare extra keys (strict equality)
+  errcount += CompareMetadataDict(baselineImageMetadata.ExtraKeys, 
+                                    testImageMetadata.ExtraKeys,
+                                    m_ReportErrors,
+                                    {});
+
+
+  if (baselineImageMetadata.Has(MDGeom::RPC))
+  {
+    if (!testImageMetadata.Has(MDGeom::RPC))
+    {
+      errcount++;
+      if (m_ReportErrors)
+      {
+        std::cerr << "Test image does not have RPC coefficients" << std::endl;
+      }
+
+    }
+    if (!(boost::any_cast<Projection::RPCParam>(baselineImageMetadata[MDGeom::RPC]) 
+          == boost::any_cast<Projection::RPCParam>(testImageMetadata[MDGeom::RPC])))
+    {
+      errcount++;
+      if (m_ReportErrors)
+      {
+        std::cerr << "RPC parameters mismatch between baseline and test images" << std::endl;
+      }
+    }
+  }
+  
   if (errcount > 0)
   {
     std::cout << "<DartMeasurement name=\"MetadataError\" type=\"numeric/int\">";
     std::cout << errcount;
     std::cout << "</DartMeasurement>" << std::endl;
   }
+
   return errcount;
 }
 
diff --git a/Modules/IO/TestKernel/src/otbTestTools.cxx b/Modules/IO/TestKernel/src/otbTestTools.cxx
index f391f1ce03b903421bee21a3c7082e6892cdb3d9..45b853276020790c91389010dbdfebc7767d9bac 100644
--- a/Modules/IO/TestKernel/src/otbTestTools.cxx
+++ b/Modules/IO/TestKernel/src/otbTestTools.cxx
@@ -79,7 +79,7 @@ void PrintMetadataBase(ImageMetadataBase imdb, std::ostream& oss)
 void PrintMetadata(ImageMetadata imd, std::ostream& oss)
 {
   PrintMetadataBase(imd, oss);
-  for (ImageMetadataBase imdb : imd.Bands)
+  for (const ImageMetadataBase & imdb : imd.Bands)
     PrintMetadataBase(imdb, oss);
 }
 
diff --git a/Modules/IO/TestKernel/test/CMakeLists.txt b/Modules/IO/TestKernel/test/CMakeLists.txt
index 2800ab8599d0d9c342c8a18c22f203f9701612f9..2320848aa4efa212b87fb85ddd3f478522a62f6e 100644
--- a/Modules/IO/TestKernel/test/CMakeLists.txt
+++ b/Modules/IO/TestKernel/test/CMakeLists.txt
@@ -29,7 +29,8 @@ set(OTBTestKernelTests
   otbCompareAsciiTests.cxx
   otbCopyTest.cxx
   otbCompareAsciiTestsEpsilon3_WhiteSpace.cxx
-  otbTestKernelTestDriver.cxx )
+  otbTestKernelTestDriver.cxx 
+  otbDummyTest.cxx)
 
 add_executable(otbTestKernelTestDriver ${OTBTestKernelTests})
 target_link_libraries(otbTestKernelTestDriver ${OTBTestKernel-Test_LIBRARIES})
@@ -205,3 +206,18 @@ otb_add_test(NAME tsTvCompareImages_DifferentSizes COMMAND otbTestKernelTestDriv
   ${TEMP}/tsTvCompareImages_DifferentSizes.tif
   )
 set_property(TEST tsTvCompareImages_DifferentSizes PROPERTY WILL_FAIL true)
+
+otb_add_test(NAME tsTvCompareMetadata1 COMMAND otbTestKernelTestDriver
+  --compare-metadata 0
+  LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
+  LARGEINPUT{SENTINEL1/S1A_S6_SLC__1SSV_20150619T195043}
+  otbDummyTest
+  )
+
+otb_add_test(NAME tsTvCompareMetadata2 COMMAND otbTestKernelTestDriver
+  --compare-metadata 0
+  ${OTB_DATA_LARGEINPUT_ROOT}/PLEIADES-PRE/TLSE_JP2_ORTHO_DIMAPv2_PMS-N_lossy_12bits/IMG_PHR1Z_PMS_N_001/IMG_PHR1A_PMS-N_201006181052297_ORT_IPU_20111011_0619-001_R1C1.JP2
+  ${OTB_DATA_LARGEINPUT_ROOT}/PLEIADES-PRE/TLSE_TIFF_ORTHO_DIMAPv2_MS_lossless_8bits/IMG_PHR1A_MS_002/IMG_PHR1A_MS_201006181052297_ORT_IPU_20111109_7807-004_R1C1.TIF
+  otbDummyTest
+  )
+set_property(TEST tsTvCompareMetadata2 PROPERTY WILL_FAIL true)
\ No newline at end of file
diff --git a/Modules/IO/TestKernel/test/otbDummyTest.cxx b/Modules/IO/TestKernel/test/otbDummyTest.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..19a8d604cd8eb917f35a3a79c9ff64148eda2939
--- /dev/null
+++ b/Modules/IO/TestKernel/test/otbDummyTest.cxx
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+ *
+ * This file is part of Orfeo Toolbox
+ *
+ *     https://www.orfeo-toolbox.org/
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+// This test does nothing. It is used to test the TestKernel module
+int otbDummyTest(int argc, char* argv[])
+{ 
+  if (argc != 1)
+  {
+    std::cerr << argv[0] << "does not take any additional parameter" << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/Modules/IO/TestKernel/test/otbTestKernelTestDriver.cxx b/Modules/IO/TestKernel/test/otbTestKernelTestDriver.cxx
index 62863ae2488982a1525099afd6d604a1378572f3..356e3c73d1ba065d4e15d69446fb1b79b201c12e 100644
--- a/Modules/IO/TestKernel/test/otbTestKernelTestDriver.cxx
+++ b/Modules/IO/TestKernel/test/otbTestKernelTestDriver.cxx
@@ -31,4 +31,5 @@ void RegisterTests()
   REGISTER_TEST(otbCompareAsciiTests);
   REGISTER_TEST(otbCompareAsciiTestsEpsilon3_WhiteSpace);
   REGISTER_TEST(otbCopyTest);
+  REGISTER_TEST(otbDummyTest);
 }
diff --git a/Modules/Registration/DisparityMap/test/otbDisparityMapToDEMFilter.cxx b/Modules/Registration/DisparityMap/test/otbDisparityMapToDEMFilter.cxx
index 91c959f7bed1aaccbf8215fcda8a5e92048a303c..31cea8f936eb4198f89ffce5a223febc650beff3 100644
--- a/Modules/Registration/DisparityMap/test/otbDisparityMapToDEMFilter.cxx
+++ b/Modules/Registration/DisparityMap/test/otbDisparityMapToDEMFilter.cxx
@@ -26,6 +26,7 @@
 #include "otbImageList.h"
 #include "otbVectorImageToImageListFilter.h"
 
+#include "otbDEMHandler.h"
 
 const unsigned int Dimension = 2;
 typedef float      PixelType;
@@ -84,7 +85,7 @@ int otbDisparityMapToDEMFilter(int argc, char* argv[])
   filter->SetElevationMax(atof(argv[8]));
   filter->SetDEMGridStep(atof(argv[10]));
 
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(atof(argv[9]));
+  otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(atof(argv[9]));
 
   MaskReaderType::Pointer maskReader;
   if (argc == 12)
diff --git a/Modules/Registration/Stereo/include/otbStereoSensorModelToElevationMapFilter.hxx b/Modules/Registration/Stereo/include/otbStereoSensorModelToElevationMapFilter.hxx
index 4222bc55dcb46177a08aa23b5d49dbaf9878ba57..f60c12cfeebbf5d654aecbefe806e1cc5a587f1e 100644
--- a/Modules/Registration/Stereo/include/otbStereoSensorModelToElevationMapFilter.hxx
+++ b/Modules/Registration/Stereo/include/otbStereoSensorModelToElevationMapFilter.hxx
@@ -259,7 +259,7 @@ void StereoSensorModelToElevationFilter<TInputImage, TOutputHeight>::BeforeThrea
   this->GetCorrelationOutput()->FillBuffer(0.);
 
   // Initialize with average elevation
-  this->GetOutput()->FillBuffer(otb::DEMHandler::Instance()->GetDefaultHeightAboveEllipsoid());
+  this->GetOutput()->FillBuffer(otb::DEMHandler::GetInstance().GetDefaultHeightAboveEllipsoid());
 
   // Initialize with DEM elevation (not threadable because of some
   // mutex in ossim)
@@ -280,7 +280,7 @@ void StereoSensorModelToElevationFilter<TInputImage, TOutputHeight>::BeforeThrea
 
     // Transform to geo point
     geoPoint = rsTransform->TransformPoint(outputPoint);
-    outputIt.Set(otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(geoPoint));
+    outputIt.Set(otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(geoPoint));
   }
 
   const InputImageType* masterPtr = this->GetMasterInput();
diff --git a/Modules/Registration/Stereo/include/otbStereorectificationDisplacementFieldSource.hxx b/Modules/Registration/Stereo/include/otbStereorectificationDisplacementFieldSource.hxx
index d31a09a94f261e97b81428560cea30adfca17d95..b6f001219adc201df5c1f38a7e962a51355e5a8a 100644
--- a/Modules/Registration/Stereo/include/otbStereorectificationDisplacementFieldSource.hxx
+++ b/Modules/Registration/Stereo/include/otbStereorectificationDisplacementFieldSource.hxx
@@ -119,7 +119,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
   m_RightImage->UpdateOutputInformation();
 
   // Setup the DEM handler if needed
-  typename DEMHandler::Pointer demHandler = DEMHandler::Instance();
+  auto & demHandler = DEMHandler::GetInstance();
 
   // Set-up a transform to use the DEMHandler
   typedef otb::GenericRSTransform<> RSTransform2DType;
@@ -156,12 +156,12 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
   outputSpacing[1] *= mean_spacing;
 
   // Then, we retrieve the origin of the left input image
-  double localElevation = otb::DEMHandler::Instance()->GetDefaultHeightAboveEllipsoid();
+  double localElevation = demHandler.GetDefaultHeightAboveEllipsoid();
 
   if (m_UseDEM)
   {
     RSTransform2DType::InputPointType tmpPoint;
-    localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(m_LeftImage->GetOrigin()));
+    localElevation = demHandler.GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(m_LeftImage->GetOrigin()));
   }
 
   TDPointType leftInputOrigin;
@@ -295,7 +295,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
   this->AllocateOutputs();
 
   // Setup the DEM handler if needed
-  typename DEMHandler::Pointer demHandler = DEMHandler::Instance();
+  auto & demHandler = DEMHandler::GetInstance();
 
   // Set-up a transform to use the DEMHandler
   typedef otb::GenericRSTransform<> RSTransform2DType;
@@ -313,7 +313,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
   TDPointType currentPoint1, currentPoint2, nextLineStart1, nextLineStart2, startLine1, endLine1, startLine2, endLine2, epiPoint1, epiPoint2;
 
   // Then, we retrieve the origin of the left input image
-  double localElevation = otb::DEMHandler::Instance()->GetDefaultHeightAboveEllipsoid();
+  double localElevation = demHandler.GetDefaultHeightAboveEllipsoid();
 
   // Use the mean spacing as before
   double mean_spacing = 0.5 * (std::abs(m_LeftImage->GetSignedSpacing()[0]) + std::abs(m_LeftImage->GetSignedSpacing()[1]));
@@ -325,7 +325,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
     RSTransform2DType::InputPointType tmpPoint;
     tmpPoint[0]    = currentPoint1[0];
     tmpPoint[1]    = currentPoint1[1];
-    localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
+    localElevation = demHandler.GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
   }
   currentPoint1[2] = localElevation;
   currentPoint2    = m_LeftToRightTransform->TransformPoint(currentPoint1);
@@ -463,7 +463,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
       RSTransform2DType::InputPointType tmpPoint;
       tmpPoint[0]    = currentPoint1[0];
       tmpPoint[1]    = currentPoint1[1];
-      localElevation = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
+      localElevation = demHandler.GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
     }
     currentPoint1[2] = localElevation;
 
@@ -489,7 +489,7 @@ void StereorectificationDisplacementFieldSource<TInputImage, TOutputImage>::Gene
         RSTransform2DType::InputPointType tmpPoint;
         tmpPoint[0]       = nextLineStart1[0];
         tmpPoint[1]       = nextLineStart1[1];
-        nextLineStart1[2] = demHandler->GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
+        nextLineStart1[2] = demHandler.GetHeightAboveEllipsoid(leftToGroundTransform->TransformPoint(tmpPoint));
       }
 
 
diff --git a/Modules/Registration/Stereo/test/otbStereoSensorModelToElevationMapFilter.cxx b/Modules/Registration/Stereo/test/otbStereoSensorModelToElevationMapFilter.cxx
index caf1ea271540720ccbc6bade977348a15ed709ad..d3081f75782c1a3a6b23349fcdb9356fb172a49b 100644
--- a/Modules/Registration/Stereo/test/otbStereoSensorModelToElevationMapFilter.cxx
+++ b/Modules/Registration/Stereo/test/otbStereoSensorModelToElevationMapFilter.cxx
@@ -66,8 +66,8 @@ int otbStereoSensorModelToElevationMapFilter(int itkNotUsed(argc), char* argv[])
   gaussian2->SetInput(slaveReader->GetOutput());
   gaussian2->SetVariance(sigma);
 
-  otb::DEMHandler::Instance()->OpenDEMDirectory(argv[4]);
-  otb::DEMHandler::Instance()->OpenGeoidFile(argv[5]);
+  otb::DEMHandler::GetInstance().OpenDEMDirectory(argv[4]);
+  otb::DEMHandler::GetInstance().OpenGeoidFile(argv[5]);
 
   StereoFilterType::Pointer stereoFilter = StereoFilterType::New();
   stereoFilter->SetMasterInput(gaussian1->GetOutput());
diff --git a/Modules/Registration/Stereo/test/otbStereorectificationDisplacementFieldSource.cxx b/Modules/Registration/Stereo/test/otbStereorectificationDisplacementFieldSource.cxx
index e6624576cf60baf00a10a3f3f921d7ba295a130e..d3a73e06d6917d6778615adb800effd01af9e7cf 100644
--- a/Modules/Registration/Stereo/test/otbStereorectificationDisplacementFieldSource.cxx
+++ b/Modules/Registration/Stereo/test/otbStereorectificationDisplacementFieldSource.cxx
@@ -55,7 +55,7 @@ int otbStereorectificationDisplacementFieldSource(int itkNotUsed(argc), char* ar
   dfSource->SetGridStep(gridStep);
   dfSource->SetScale(scale);
 
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(avgElev);
+  otb::DEMHandler::GetInstance().SetDefaultHeightAboveEllipsoid(avgElev);
 
   WriterType::Pointer writer1 = WriterType::New();
   writer1->SetInput(dfSource->GetLeftDisplacementFieldOutput());
diff --git a/Modules/Remote/S1TilingSupportApplications.remote.cmake b/Modules/Remote/S1TilingSupportApplications.remote.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..7bafd6d7c2cf6bbab0c34b582fa56b0681c04d4e
--- /dev/null
+++ b/Modules/Remote/S1TilingSupportApplications.remote.cmake
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
+#
+# This file is part of Orfeo Toolbox
+#
+#     https://www.orfeo-toolbox.org/
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# Contact: Thierry Koleck  <thierry.koleck@cnes.fr>
+
+
+otb_fetch_module(S1TilingSupportApplications
+  "Speckle filtering of a time-serie of SAR images using the multichanel Quegan-like filter
+A more detailed description can be found on the project website:
+https://gitlab.orfeo-toolbox.org/s1-tiling/s1tilingsupportapplications
+"
+  GIT_REPOSITORY https://gitlab.orfeo-toolbox.org/s1-tiling/s1tilingsupportapplications
+  # Commit on master branch
+  GIT_TAG 2.0.0
+)
diff --git a/Modules/Remote/diapotb.remote.cmake b/Modules/Remote/diapotb.remote.cmake
index 0f9791f05cde1240ae062f84433681529b92e41c..21ce4768f6a70ca4ced99f4812c6a386f8cf3c00 100644
--- a/Modules/Remote/diapotb.remote.cmake
+++ b/Modules/Remote/diapotb.remote.cmake
@@ -22,5 +22,5 @@
 otb_fetch_module(DiapOTBModule
   "OTB module for SAR processing in Diapason."
   GIT_REPOSITORY https://gitlab.orfeo-toolbox.org/remote_modules/diapotb.git
-  GIT_TAG f84b85352abb75ff3eefafdf0480fcde7fa06173
+  GIT_TAG d55001b44df1a9c4a1c1d542dfd1362003bb0b51
 )
diff --git a/Modules/ThirdParty/6S/src/6SV1.0B/AEROSO.c b/Modules/ThirdParty/6S/src/6SV1.0B/AEROSO.c
index 6f4d8070e7233bf2e9b08f570b18cb33bfc48b4f..3cd88258102e08735ee71fe70b67bcd94d51cc73 100644
--- a/Modules/ThirdParty/6S/src/6SV1.0B/AEROSO.c
+++ b/Modules/ThirdParty/6S/src/6SV1.0B/AEROSO.c
@@ -19,7 +19,7 @@ Extern struct {
 #define num_quad__1 num_quad__
 
 /*OTB */
-/*Extern*/ struct {
+Extern struct {
     doublereal ph[20000]	/* was [20][1000] */, qh[20000]	/* was [20][
 	    1000] */, uh[20000]	/* was [20][1000] */;
 } sixs_aerbas__;
@@ -27,7 +27,7 @@ Extern struct {
 #define sixs_aerbas__1 sixs_aerbas__
 
 /*OTB */
-/*Extern*/ struct {
+Extern struct {
     doublereal phasel[20000]	/* was [20][1000] */, qhasel[20000]	/* 
 	    was [20][1000] */, uhasel[20000]	/* was [20][1000] */;
 } sixs_phase__;
@@ -35,14 +35,14 @@ Extern struct {
 #define sixs_phase__1 sixs_phase__
 
 /*OTB */
-/*Extern*/ struct {
+Extern struct {
     doublereal ext[20], ome[20], gasym[20], phase[20], qhase[20], uhase[20];
 } sixs_aer__;
 
 #define sixs_aer__1 sixs_aer__
 
 /*OTB */
-/*Extern*/ struct {
+Extern struct {
     doublereal ex[80]	/* was [4][20] */, sc[80]	/* was [4][20] */, 
 	    asy[80]	/* was [4][20] */, vi[4];
 } sixs_coef__;
diff --git a/Modules/ThirdParty/6S/src/6SV1.0B/DISCOM.c b/Modules/ThirdParty/6S/src/6SV1.0B/DISCOM.c
index 4057cba8d169764bd54345ed50b5ab15b3ecac85..c331b2d1053b50f2deb36dea2fc0cda6410717c8 100644
--- a/Modules/ThirdParty/6S/src/6SV1.0B/DISCOM.c
+++ b/Modules/ThirdParty/6S/src/6SV1.0B/DISCOM.c
@@ -26,7 +26,7 @@ Extern struct {
 #define sixs_phase__1 sixs_phase__
 
 /*OTB */
-/*Extern*/ struct {
+Extern struct {
     doublereal pha[1000], qha[1000], uha[1000], alphal[1001], betal[1001], 
 	    gammal[1001], zetal[1001];
 } sixs_polar__;
diff --git a/Modules/ThirdParty/6S/src/CMakeLists.txt b/Modules/ThirdParty/6S/src/CMakeLists.txt
index e174445c96777dc7ab7360a8d4440e39f5dc7b21..013d404e7ecd17e1b08967844596745c57bcfe90 100644
--- a/Modules/ThirdParty/6S/src/CMakeLists.txt
+++ b/Modules/ThirdParty/6S/src/CMakeLists.txt
@@ -66,6 +66,20 @@ list(REMOVE_ITEM LIBF2C_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/libf2c/main.c" )
 list(REMOVE_ITEM LIBF2C_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/libf2c/getarg_.c" )
 list(REMOVE_ITEM LIBF2C_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/libf2c/iargc_.c" )
 
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/aeroprof_com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/multorder_com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_del__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_ier__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/num_quad__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_aer__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_disc__com.c" )
+
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_planesim__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/mie_in__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_atm__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_ffu__com.c" )
+list(REMOVE_ITEM OTB6S_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/6SV1.0B/sixs_test__com.c" )
+
 # Main code source
 set(OTB6S_MAIN_SRCS
   otb_main6S_function.c                # otb 6S main function
diff --git a/Modules/ThirdParty/6S/src/otb_main6S_function.c b/Modules/ThirdParty/6S/src/otb_main6S_function.c
index af18dffe850da0e4c6c8a7642a467e5b9ee636b5..bde8627b6396064ebb231475fccebfb601878a72 100644
--- a/Modules/ThirdParty/6S/src/otb_main6S_function.c
+++ b/Modules/ThirdParty/6S/src/otb_main6S_function.c
@@ -13,7 +13,7 @@ extern "C" {
 /* Common Block Declarations */
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     integer nquad;
 } num_quad__;
@@ -21,7 +21,7 @@ struct {
 #define num_quad__1 num_quad__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     integer num_z__;
     doublereal alt_z__[101], taer_z__[101], taer55_z__[101];
@@ -30,7 +30,7 @@ struct {
 #define aeroprof_1 aeroprof_
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     integer iwr;
     logical ier;
@@ -39,7 +39,7 @@ struct {
 #define sixs_ier__1 sixs_ier__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal rmax, rmin;
     integer icp;
@@ -52,7 +52,7 @@ struct {
 #define mie_in__1 mie_in__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     integer igmax;
 } multorder_;
@@ -60,7 +60,7 @@ struct {
 #define multorder_1 multorder_
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal zpl[34], ppl[34], tpl[34], whpl[34], wopl[34];
 } sixs_planesim__;
@@ -68,7 +68,7 @@ struct {
 #define sixs_planesim__1 sixs_planesim__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal xacc;
 } sixs_test__;
@@ -76,7 +76,7 @@ struct {
 #define sixs_test__1 sixs_test__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal s[1501], wlinf, wlsup;
 } sixs_ffu__;
@@ -84,7 +84,7 @@ struct {
 #define sixs_ffu__1 sixs_ffu__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal delta, sigma;
 } sixs_del__;
@@ -92,7 +92,7 @@ struct {
 #define sixs_del__1 sixs_del__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal z__[34], p[34], t[34], wh[34], wo[34];
 } sixs_atm__;
@@ -100,7 +100,7 @@ struct {
 #define sixs_atm__1 sixs_atm__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal ext[20], ome[20], gasym[20], phase[20], qhase[20], uhase[20];
 } sixs_aer__;
@@ -108,7 +108,7 @@ struct {
 #define sixs_aer__1 sixs_aer__
 
 /*OTB */
-/*Extern*/
+Extern
 struct {
     doublereal roatm[60]	/* was [3][20] */, dtdir[60]	/* was [3][20]
 	     */, dtdif[60]	/* was [3][20] */, utdir[60]	/* was [3][20]
diff --git a/Modules/ThirdParty/Boost/otb-module-init.cmake b/Modules/ThirdParty/Boost/otb-module-init.cmake
index c7b4baf1dd6b89fa0e72a08abaf6e9ff6255ca64..9935be9c41e55f7a74becb331fd79d3207b10a23 100644
--- a/Modules/ThirdParty/Boost/otb-module-init.cmake
+++ b/Modules/ThirdParty/Boost/otb-module-init.cmake
@@ -22,9 +22,9 @@ set(Boost_USE_STATIC_LIBS OFF CACHE BOOL "use static libraries from boost")
 
 set(Boost_USE_MULTITHREADED ON CACHE BOOL "use multi-threaded libraries from boost")
 
-find_package (Boost 1.35.0 REQUIRED)
+find_package (Boost 1.35.0 REQUIRED COMPONENTS filesystem)
 if (BUILD_TESTING)
-  find_package (Boost 1.35.0 QUIET COMPONENTS unit_test_framework)
+  find_package (Boost 1.35.0 QUIET COMPONENTS filesystem unit_test_framework)
   if (NOT Boost_UNIT_TEST_FRAMEWORK_FOUND)
     message(STATUS "Boost unit_test_framework not found. Hence otbOGRTests will be skipped")
   else()
diff --git a/Modules/ThirdParty/OssimPlugins/src/ossim/AlosPalsar/AlosPalsarData.h b/Modules/ThirdParty/OssimPlugins/src/ossim/AlosPalsar/AlosPalsarData.h
index 2822a4fd68d58bb1369e2c1e3c7867e8ef217309..efd2e2801651cd3ff78cdb9b1afbe88e4abdd9e1 100644
--- a/Modules/ThirdParty/OssimPlugins/src/ossim/AlosPalsar/AlosPalsarData.h
+++ b/Modules/ThirdParty/OssimPlugins/src/ossim/AlosPalsar/AlosPalsarData.h
@@ -28,6 +28,7 @@
 #define AlosPalsarData_h
 
 #include <map>
+#include <iostream>
 
 class ossimKeywordlist;
 
diff --git a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimTimeUtilities.cpp b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimTimeUtilities.cpp
index 3abe9aecaa44bc6272371634143824595c793e9b..90688a43e2d033997bc0b0bcc0bf454a81600ec3 100644
--- a/Modules/ThirdParty/OssimPlugins/src/ossim/ossimTimeUtilities.cpp
+++ b/Modules/ThirdParty/OssimPlugins/src/ossim/ossimTimeUtilities.cpp
@@ -83,7 +83,7 @@ ossimDate ossimplugins::time::details::strptime(string_view const& format, strin
                          }
                          break;
                default:
-                  throw std::logic_error("Unsupported date format speficier (in "+format+": "+fmt.front()+")");
+                  throw std::logic_error("Unsupported date format specifier (in "+format+": "+fmt.front()+")");
             }
             break;
          default:
diff --git a/Modules/Visualization/IceViewer/src/otbIce.cxx b/Modules/Visualization/IceViewer/src/otbIce.cxx
index 4afffe3abdecb2930813e3a960ec4421b5c3985d..96704dd5a32bccf3871ce63fbe03d4d7dcace44a 100644
--- a/Modules/Visualization/IceViewer/src/otbIce.cxx
+++ b/Modules/Visualization/IceViewer/src/otbIce.cxx
@@ -39,18 +39,18 @@ int main(int argc, char* argv[])
   char* demdir    = getenv("OTB_DEM_DIR");
   char* geoidfile = getenv("OTB_GEOID_FILE");
 
-  otb::DEMHandler::Pointer demHandler = otb::DEMHandler::Instance();
+  auto& demHandler = otb::DEMHandler::GetInstance();
 
   if (demdir != nullptr)
   {
     std::cout << "Configuring DEM directory: " << demdir << std::endl;
-    demHandler->OpenDEMDirectory(demdir);
+    demHandler.OpenDEMDirectory(demdir);
   }
 
   if (geoidfile != nullptr)
   {
     std::cout << "Configuring geoid file: " << geoidfile << std::endl;
-    demHandler->OpenGeoidFile(geoidfile);
+    demHandler.OpenGeoidFile(geoidfile);
   }
 
   otb::IceViewer::Pointer viewer = otb::IceViewer::New();
diff --git a/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h b/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h
index 662ae8f26ce52395715b34d0427bc2aacf0ee45f..b3f47ec26f03fdf45ec34fef5729a03a8ea94ede 100644
--- a/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h
+++ b/Modules/Visualization/MonteverdiCore/include/mvdHistogramModel.h
@@ -433,9 +433,9 @@ HistogramModel
 template <typename TImageModel>
 void HistogramModel::template_BuildModel_M(BuildContext*)
 {
-  QTime lMain;
-  QTime lPass1;
-  QTime lPass2;
+  QElapsedTimer lMain;
+  QElapsedTimer lPass1;
+  QElapsedTimer lPass2;
 
   lMain.start();
 
diff --git a/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx b/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx
index e72f6d7b3a638f81d0e5a0f365a7cad40af8b090..658600e6422fc36da73b70ac8d2a691a1ef8335b 100644
--- a/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx
+++ b/Modules/Visualization/MonteverdiCore/src/mvdHistogramModel.cxx
@@ -332,7 +332,7 @@ void HistogramModel::virtual_Read(QIODevice* device)
 
   qDebug() << tr("%1: Reading histogram...").arg(QDateTime::currentDateTime().toString(Qt::ISODate));
 
-  QTime time;
+  QElapsedTimer time;
   time.start();
 
 
@@ -385,7 +385,7 @@ void HistogramModel::virtual_Write(QIODevice& device) const
 
   qDebug() << tr("%1: Writing histogram...").arg(QDateTime::currentDateTime().toString(Qt::ISODate));
 
-  QTime time;
+  QElapsedTimer time;
   time.start();
 
 #if 0
diff --git a/Modules/Visualization/MonteverdiCore/src/mvdI18nCoreApplication.cxx b/Modules/Visualization/MonteverdiCore/src/mvdI18nCoreApplication.cxx
index 3f8d98080dedf7916dd526b8a12ef8ed2f818d6f..0df1eb5043b9f55e121c22f48f4caff8f5ccdf84 100644
--- a/Modules/Visualization/MonteverdiCore/src/mvdI18nCoreApplication.cxx
+++ b/Modules/Visualization/MonteverdiCore/src/mvdI18nCoreApplication.cxx
@@ -348,9 +348,7 @@ void I18nCoreApplication::SetModel(AbstractModel* model)
 /*******************************************************************************/
 bool I18nCoreApplication::ElevationSetup()
 {
-  assert(!otb::DEMHandler::Instance().IsNull());
-
-  otb::DEMHandler::Pointer demHandlerInstance(otb::DEMHandler::Instance());
+  auto & demHandlerInstance = otb::DEMHandler::GetInstance();
 
   bool geoidUpdated = false;
 
@@ -363,7 +361,7 @@ bool I18nCoreApplication::ElevationSetup()
     {
       QString filename(I18nCoreApplication::RetrieveSettingsKey(I18nCoreApplication::SETTINGS_KEY_GEOID_PATH).toString());
 
-      geoidUpdated = demHandlerInstance->OpenGeoidFile(QFile::encodeName(filename));
+      geoidUpdated = demHandlerInstance.OpenGeoidFile(QFile::encodeName(filename).toStdString());
 
       // BUGFIX: When geoid file has not been updated by
       // otb::DEMHandler, the filename may be erroneous and unchecked
@@ -397,9 +395,9 @@ bool I18nCoreApplication::ElevationSetup()
 
     try
     {
-      demHandlerInstance->ClearDEMs();
+      demHandlerInstance.ClearDEMs();
 
-      demHandlerInstance->OpenDEMDirectory(QFile::encodeName(I18nCoreApplication::RetrieveSettingsKey(I18nCoreApplication::SETTINGS_KEY_SRTM_DIR).toString()));
+      demHandlerInstance.OpenDEMDirectory(QFile::encodeName(I18nCoreApplication::RetrieveSettingsKey(I18nCoreApplication::SETTINGS_KEY_SRTM_DIR).toString()).toStdString());
     }
     catch (const std::exception& err)
     {
@@ -413,7 +411,7 @@ bool I18nCoreApplication::ElevationSetup()
   }
   else
   {
-    otb::DEMHandler::Instance()->ClearDEMs();
+    otb::DEMHandler::GetInstance().ClearDEMs();
   }
 
   return geoidUpdated;
diff --git a/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx b/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx
index a49e43eb68aee6cd2556625b96082723fcdf89d2..cb4d35168cfc425b84472d702008b46459b302a6 100644
--- a/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx
+++ b/Modules/Visualization/MonteverdiCore/src/mvdVectorImageModel.cxx
@@ -603,7 +603,7 @@ void VectorImageModel::virtual_ToWgs84(const PointType& physical, PointType& wgs
 
   wgs84 = m_ToWgs84->TransformPoint(physical);
 
-  alt = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(wgs84[0], wgs84[1]);
+  alt = otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(wgs84[0], wgs84[1]);
 }
 
 /*****************************************************************************/
@@ -735,7 +735,7 @@ void VectorImageModel::OnPhysicalCursorPositionChanged(const QPoint&, const Poin
         geoVector.push_back(ossGeographicLong.str());
         geoVector.push_back(ossGeographicLat.str());
 
-        double elev = otb::DEMHandler::Instance()->GetHeightAboveEllipsoid(wgs84[0], wgs84[1]);
+        double elev = otb::DEMHandler::GetInstance().GetHeightAboveEllipsoid(wgs84[0], wgs84[1]);
 
         if (elev > -32768)
         {
diff --git a/Modules/Visualization/MonteverdiGui/src/mvdHistogramPlotPicker.cxx b/Modules/Visualization/MonteverdiGui/src/mvdHistogramPlotPicker.cxx
index baf0cdb134dca30d4b30a2d458a6e401dba9d9a9..badc027e1626b69796152686320d1db8a2b9aeb5 100644
--- a/Modules/Visualization/MonteverdiGui/src/mvdHistogramPlotPicker.cxx
+++ b/Modules/Visualization/MonteverdiGui/src/mvdHistogramPlotPicker.cxx
@@ -214,10 +214,10 @@ QwtText HistogramPlotPicker::trackerTextF(const QPointF& point) const
     return QwtPlotPicker::trackerTextF(point);
 
   if (!isActive())
-    return QwtText(QString().sprintf("%.4f, %.4f", point.x(), point.y()));
+    return QwtText(QString("%.4f, %.4f").arg(point.x()).arg(point.y()));
 
   QString text;
-  text.sprintf("%.4f", point.x());
+  text = QString("%.4f").arg(point.x());
 
   CountType start = 0;
   CountType stop  = 0;
@@ -232,7 +232,7 @@ QwtText HistogramPlotPicker::trackerTextF(const QPointF& point) const
 
         Find(m_PlotCurves[i], point.x(), c0, c1, cf);
 
-        text.append(QString().sprintf("\n[%.4f; %.4f[, %.0f", c0, c1, cf));
+        text.append(QString("\n[%.4f; %.4f[, %.0f").arg(c0).arg(c1).arg(cf));
       }
 
   return text;
diff --git a/Modules/Wrappers/ApplicationEngine/src/otbWrapperElevationParametersHandler.cxx b/Modules/Wrappers/ApplicationEngine/src/otbWrapperElevationParametersHandler.cxx
index 94997bd5a916143b4f6e70208daf233dd9a13f16..33072efb089c4ec94c3e015b7806023ca8f66b48 100644
--- a/Modules/Wrappers/ApplicationEngine/src/otbWrapperElevationParametersHandler.cxx
+++ b/Modules/Wrappers/ApplicationEngine/src/otbWrapperElevationParametersHandler.cxx
@@ -21,6 +21,7 @@
 #include "otbWrapperElevationParametersHandler.h"
 #include "otbDEMHandler.h"
 
+
 namespace otb
 {
 namespace Wrapper
@@ -98,8 +99,11 @@ void ElevationParametersHandler::AddElevationParameters(Application::Pointer app
 
 void ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(const Application::Pointer app, const std::string& key)
 {
+  auto & demHandler = otb::DEMHandler::GetInstance();
+
   // Set default elevation
-  otb::DEMHandler::Instance()->SetDefaultHeightAboveEllipsoid(GetDefaultElevation(app, key));
+  demHandler.SetDefaultHeightAboveEllipsoid(GetDefaultElevation(app, key));
+
 
   std::ostringstream oss;
   oss << "Elevation management: setting default height above ellipsoid to " << GetDefaultElevation(app, key) << " meters" << std::endl;
@@ -111,7 +115,8 @@ void ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(const Ap
   {
     oss.str("");
     oss << "Elevation management: using geoid file (" << GetGeoidFile(app, key) << ")" << std::endl;
-    otb::DEMHandler::Instance()->OpenGeoidFile(GetGeoidFile(app, key));
+    demHandler.OpenGeoidFile(GetGeoidFile(app, key));
+    
     app->GetLogger()->Info(oss.str());
   }
 
@@ -119,11 +124,11 @@ void ElevationParametersHandler::SetupDEMHandlerFromElevationParameters(const Ap
   if (IsDEMUsed(app, key))
   {
     std::string demDirectory = GetDEMDirectory(app, key);
-    if (otb::DEMHandler::Instance()->IsValidDEMDirectory(demDirectory.c_str()))
+    if (demHandler.IsValidDEMDirectory(demDirectory.c_str()))
     {
       oss.str("");
       oss << "Elevation management: using DEM directory (" << demDirectory << ")" << std::endl;
-      otb::DEMHandler::Instance()->OpenDEMDirectory(demDirectory);
+      demHandler.OpenDEMDirectory(demDirectory);
       app->GetLogger()->Info(oss.str());
     }
     else
diff --git a/Modules/Wrappers/SWIG/src/otbApplication.i b/Modules/Wrappers/SWIG/src/otbApplication.i
index 34b964dc5142d7b08698fa753212d75fd6f1568a..03ecf560ce300f7a82398986bcb5d4b080523ea0 100644
--- a/Modules/Wrappers/SWIG/src/otbApplication.i
+++ b/Modules/Wrappers/SWIG/src/otbApplication.i
@@ -19,7 +19,7 @@
  */
 
 
-%module otbApplication
+%module("threads"=1) otbApplication
 
 %{
 #include "itkBase.includes"
@@ -259,22 +259,22 @@ public:
 #if SWIGPYTHON
   Logger* GetLogger();
 #endif
-  unsigned long itk::Object::AddObserver(const EventObject & event, 
+  unsigned long itk::Object::AddObserver(const EventObject & event,
                                           Command * command);
 
   bool IsDeprecated();
 
 #if SWIGPYTHON
-  %extend 
+  %extend
     {
-    /** SetupLogger : Add the PythonLogOutput and setup the progress 
+    /** SetupLogger : Add the PythonLogOutput and setup the progress
      * reporting for the application */
     %pythoncode
       {
       def SetupLogger(self):
           logger = self.GetLogger()
           logger.AddLogOutput(_libraryLogOutput.GetPointer())
-          
+
           self.progressReportManager = ProgressReporterManager_New()
           self.progressReportManager.SetLogOutputCallback(_libraryLogCallback)
           self.AddObserver(AddProcessToWatchEvent(),
@@ -726,7 +726,7 @@ class ApplicationProxy(object):
       """
       if (name == "thisown"):
         return self.this.own()
-        
+
       if hasattr(Application, "__swig_getmethods__"):
         method = Application.__swig_getmethods__.get(name, None)
         if method:
@@ -757,7 +757,7 @@ class ApplicationProxy(object):
       if (name == "progressReportManager"):
         super().__setattr__(name, value)
         return
-      
+
       if hasattr(Application, "__swig_setmethods__"):
         method = Application.__swig_setmethods__.get(name, None)
         if method:
@@ -813,7 +813,7 @@ class ApplicationProxy(object):
       ImagePixelType_cfloat : SetVectorImageFromCFloatNumpyArray_,
       ImagePixelType_cdouble : SetVectorImageFromCDoubleNumpyArray_,
       }
-    
+
     def SetImageFromNumpyArray(self, paramKey, npArray, index=0):
       """
       This method takes a numpy array and set ImageIOBase of
diff --git a/Modules/Wrappers/SWIG/src/python/itkPyCommand.cxx b/Modules/Wrappers/SWIG/src/python/itkPyCommand.cxx
index 9f6acb0104b05d07bbeea2fcdfd61660ccd2c03c..23c417949dfe3ebb48c397c6cf031ae6f956f850 100644
--- a/Modules/Wrappers/SWIG/src/python/itkPyCommand.cxx
+++ b/Modules/Wrappers/SWIG/src/python/itkPyCommand.cxx
@@ -21,6 +21,27 @@
 
 #include "itkPyCommand.h"
 
+namespace
+{
+// Wrapper to automatics obtain and release GIL
+// RAII idiom
+class PyGILStateEnsure
+{
+public:
+  PyGILStateEnsure()
+    {
+      m_GIL = PyGILState_Ensure();
+    }
+  ~PyGILStateEnsure()
+    {
+      PyGILState_Release(m_GIL);
+    }
+private:
+  PyGILState_STATE m_GIL;
+};
+} // end anonymous namespace
+
+
 namespace itk
 {
 
@@ -33,6 +54,7 @@ PyCommand::~PyCommand()
 {
   if (this->obj)
   {
+    PyGILStateEnsure gil;
     Py_DECREF(this->obj);
   }
   this->obj = nullptr;
@@ -42,6 +64,7 @@ void PyCommand::SetCommandCallable(PyObject* theObj)
 {
   if (theObj != this->obj)
   {
+    PyGILStateEnsure gil;
     if (this->obj)
     {
       // get rid of our reference
@@ -89,6 +112,7 @@ void PyCommand::PyExecute()
   }
   else
   {
+    PyGILStateEnsure gil;
     PyObject* result;
 
     result = PyEval_CallObject(this->obj, (PyObject*)nullptr);
diff --git a/Modules/Wrappers/SWIG/src/python/otbPythonLogOutput.cxx b/Modules/Wrappers/SWIG/src/python/otbPythonLogOutput.cxx
index a924de7a165950cc42e840de268f4fc989a4da14..6a36c88c4cb8a2f99532188d068d718c3d623287 100644
--- a/Modules/Wrappers/SWIG/src/python/otbPythonLogOutput.cxx
+++ b/Modules/Wrappers/SWIG/src/python/otbPythonLogOutput.cxx
@@ -34,6 +34,8 @@ void PythonLogOutput::Write(double timestamp)
 
 void PythonLogOutput::Write(std::string const& content)
 {
+  // Note : Reacuiring the GIL before calling the callback doesn' seems to be
+  // necessary with thread=1 and director classes
   m_Callback->Call(content);
 }
 
diff --git a/Packaging/CMakeLists.txt b/Packaging/CMakeLists.txt
index 4a92a0d123aa2bac513e92b2a96308cf8d75563b..e4ba25158fed15615e4dfca3be97326814174762 100644
--- a/Packaging/CMakeLists.txt
+++ b/Packaging/CMakeLists.txt
@@ -106,11 +106,8 @@ if(OTB_TARGET_SYSTEM_ARCH_IS_X64)
 set(PACKAGE_ARCH "64")
 endif()
 
-
 include(${PACKAGE_OTB_SRC_DIR}/CMake/GetVersionFromGitTag.cmake)
-get_package_name(${PACKAGE_OTB_SRC_DIR} OTB_VERSION_STRING2)
-message(STATUS "OTB_VERSION_STRING2 : ${OTB_VERSION_STRING2}")
-
+get_package_name(${PACKAGE_OTB_SRC_DIR} PKG_OTB OTB_VERSION_STRING2)
 
 # This directory is important.
 # We stage/keep files that goes into final package in this directory
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index ff9974ee400b20c09cc65b642d9ce48b8aab3221..50a130783ab0c52a9b73f9363ee5eab16af13247 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,3 +1,51 @@
+OTB-v 7.2.0 - Changes since version 7.1.0 (October 02nd, 2020)
+----------------------------------------------------------------
+
+Features added:
+
+   * !736: Integrate S1Tiling support apps as official remote module by Luc Hermitte
+   * !709: Add \"epsg\" filename extension by Julien Osman
+   * !708: Spectral angle classification by Cédric Traizet
+   * !704: Pantex texture extraction application by Cédric Traizet
+
+Bugs fixed:
+
+   * !726: "Release the Python Global Interpreter Lock (GIL) during execution" by Luc Hermitte, Julien Osman and Cédric Traizet
+   * !730: Freetype and libPNG detection in QT5 by Cédric Traizet
+   * !727: FIX: StreamingMosaicFilter childs can have a different number of components per pixel at output by Rémi Cresson
+   * !725: Fix superbuild with OpenCV4 by Cédric Traizet
+   * !720: Fix spatial reference equality tests by Cédric Traizet
+   * !714: BUG: #2046 Add an epsilon margin to compute the m_ReachableOutputRegion by Julien Osman
+   * !705: Initialize seed with std::time if not user-supplied by Guillaume Pernot
+   * !696: Fix warnings by Cédric Traizet
+
+Refactoring:
+
+   * !731: Update MuParser in Superbuild by Cédric Traizet and Julie Brossard
+   * !715: Update GDAL version in superbuild (3.1.0) by Cédric Traizet
+   * !711: Updates QT5, ITK and pcre by Cédric Traizet
+   * !698: ENH: Use std::move instead of boost::move by Laurențiu Nicola
+   * !694: Update ul_lon/lat, ur_lon/lat, lr_lon/lat and ll_lon/lat for CosmoSkymed Sensor by Gaëlle USSEGLIO
+   * !683: Refactor compare image by Cédric Traizet
+
+CI:
+
+   * !721: Deploy Doxygen documentation in CI by Cédric Traizet
+   * !719: Fix Centos CI build by Cédric Traizet
+
+Documentation:
+
+   * !754: Fix spelling errors by Bas Couwenberg
+   * !738: Update PSC informations by Julien Osman
+   * !735: Correct menu instructions to open application-browser in Monteverdi by Julien Osman
+   * !728: DOC: Deprecate methods that will be removed in OTB 8.0.0 by Julien Osman
+   * !722: Clean QGIS interface documentation according migration into QGIS documentation by Mickael Savinaud
+   * !716: Error in the documenation of the QGIS plugin by Cédric Traizet
+   * !713: Update documentation for QGIS, Python and multiwriter by Cédric Traizet
+   * !702: DOC: correct filthy typo by Rémi Cresson
+   * !695: DOC: Fixed typos by guillaume pernot
+
+
 OTB-v 7.1.0 - Changes since version 7.0.0 (March 16th, 2020)
 ----------------------------------------------------------------
 
diff --git a/SuperBuild/CMake/External_gdal.cmake b/SuperBuild/CMake/External_gdal.cmake
index d099e1d63d94388cc8477714f564e2fa32e0c3b0..0ad9f33756eaccafc39bf568ac6a8c4f1cdb4262 100644
--- a/SuperBuild/CMake/External_gdal.cmake
+++ b/SuperBuild/CMake/External_gdal.cmake
@@ -81,6 +81,7 @@ if(UNIX)
     --with-ingres=no
     --with-jp2mrsid=no
     --with-kakadu=no
+    --with-kea=no
     --with-jasper=no
     --with-libgrass=no
     --with-mrsid=no
diff --git a/SuperBuild/CMake/External_itk.cmake b/SuperBuild/CMake/External_itk.cmake
index 491b68fa869dd4b8e5613a8202f0ec9a8143fb80..7d1d09d0d1398cb9796bb98f32d811e7e90c162d 100644
--- a/SuperBuild/CMake/External_itk.cmake
+++ b/SuperBuild/CMake/External_itk.cmake
@@ -146,8 +146,8 @@ set(_SB_ITK_DIR ${SB_INSTALL_PREFIX}/lib/cmake/ITK-${SB_ITK_VERSION_MAJOR}.${SB_
 
 ExternalProject_Add(ITK
   PREFIX ITK
-  URL "https://sourceforge.net/projects/itk/files/itk/4.13/InsightToolkit-4.13.2.tar.gz"
-  URL_MD5 1b6ae17dbd605d6c36564cc3d2cc1ee8
+  URL "https://github.com/InsightSoftwareConsortium/ITK/archive/v4.13.3.tar.gz"
+  URL_MD5 cc0e2d9b243f28db84b8b4a45a23f9d7
   SOURCE_DIR ${ITK_SB_SRC}
   BINARY_DIR ${ITK_SB_BUILD_DIR}
   INSTALL_DIR ${SB_INSTALL_PREFIX}
diff --git a/SuperBuild/CMake/External_openssl.cmake b/SuperBuild/CMake/External_openssl.cmake
index 6f27644f0a396a31769097eb201c23a9709ebe58..e33bb5d1e22ed8d355337ac511ba9ada20e3d0fd 100644
--- a/SuperBuild/CMake/External_openssl.cmake
+++ b/SuperBuild/CMake/External_openssl.cmake
@@ -77,6 +77,7 @@ else(UNIX)
     ${SB_ENV_CONFIGURE_CMD}
     ${OPENSSL_SB_SRC}/config ${OPENSSL_BUILD_ARCH}
     "--prefix=${SB_INSTALL_PREFIX}"
+    "--libdir=lib"
     shared
     zlib
     zlib-dynamic
diff --git a/SuperBuild/CMake/External_proj.cmake b/SuperBuild/CMake/External_proj.cmake
index dc014e321a2d7ed19ea2aa4b690d354891ad5672..d44c76b5cdb078be739cc86990d057cc04dbbabf 100644
--- a/SuperBuild/CMake/External_proj.cmake
+++ b/SuperBuild/CMake/External_proj.cmake
@@ -28,7 +28,7 @@ ADDTO_DEPENDENCIES_IF_NOT_SYSTEM(PROJ SQLITE)
 ExternalProject_Add(PROJ
   DEPENDS ${PROJ_DEPENDENCIES}
   PREFIX PROJ
-  URL "http://download.osgeo.org/proj/proj-6.2.1.tar.gz"
+  URL "http://download.osgeo.org/proj/proj-6.2.1.tar.gz"  # If we update PROJ to v7+, check option PROJ_TESTS!
   URL_MD5 9f874e227d221daf95f7858dc55dfa3e
   BINARY_DIR ${PROJ_SB_SRC}
   INSTALL_DIR ${SB_INSTALL_PREFIX}
@@ -40,6 +40,7 @@ ExternalProject_Add(PROJ
   -DBUILD_FRAMEWORKS_AND_BUNDLE:BOOL=FALSE
   -DPROJ_LIB_SUBDIR:STRING=lib
   -DPROJ_INCLUDE_SUBDIR:STRING=include
+  -DPROJ_TESTS:BOOL=OFF  # Starting with PROJ 7.0, the PROJ_TESTS option has been renamed into BUILD_TESTING
   CMAKE_COMMAND ${SB_CMAKE_COMMAND}
   LOG_DOWNLOAD 1
   LOG_CONFIGURE 1
diff --git a/SuperBuild/CMake/External_qt5.cmake b/SuperBuild/CMake/External_qt5.cmake
index 0cac35eaa222b28b4bf05ff6516e40bc08092d05..3991df39761a12048443b8e17ab80c1ae4b00c59 100644
--- a/SuperBuild/CMake/External_qt5.cmake
+++ b/SuperBuild/CMake/External_qt5.cmake
@@ -98,7 +98,11 @@ if(UNIX)
     set(QT5_SB_CONFIG "${QT5_SB_CONFIG} -no-framework")
   else()
       #Linux
-      set(QT5_SB_CONFIG "${QT5_SB_CONFIG} -no-glib -no-fontconfig")
+      # -no-use-gold-linker: https://bugreports.qt.io/browse/QTBUG-66571 
+      # Without this option the gold linker is forced if it is available
+      # In more recent versions the default linker is used instead, and this
+      # option has been removed.
+      set(QT5_SB_CONFIG "${QT5_SB_CONFIG} -no-glib -no-fontconfig -no-use-gold-linker")
   endif()
   #common for all unix
   set(QT5_SB_CONFIG "${QT5_SB_CONFIG} -no-dbus -no-icu -v")
@@ -130,8 +134,8 @@ configure_file( ${QT5_CONFIGURE_COMMAND_IN} ${QT5_CONFIGURE_COMMAND} @ONLY )
 
 ExternalProject_Add(QT5
   PREFIX QT5
-  URL "https://download.qt.io/archive/qt/5.14/5.14.1/single/qt-everywhere-src-5.14.1.tar.xz"
-  URL_MD5 781c3179410aff7ef84607214e1e91b4
+  URL "https://download.qt.io/new_archive/qt/5.11/5.11.3/single/qt-everywhere-src-5.11.3.tar.xz"
+  URL_MD5 02b353bfe7a40a8dc4274e1d17226d2b
   BINARY_DIR ${QT5_SB_BUILD_DIR}
   INSTALL_DIR ${SB_INSTALL_PREFIX}
   DOWNLOAD_DIR ${DOWNLOAD_LOCATION}
diff --git a/SuperBuild/CMake/External_qwt.cmake b/SuperBuild/CMake/External_qwt.cmake
index 9e97c83228f7d17c30c31c2695cf37d6f69633d1..0bf4da29f63bd9cec07fc8ffc04391a0f7dcaa40 100644
--- a/SuperBuild/CMake/External_qwt.cmake
+++ b/SuperBuild/CMake/External_qwt.cmake
@@ -55,8 +55,8 @@ endif()
 
 ExternalProject_Add(QWT
   PREFIX QWT
-  URL "http://downloads.sourceforge.net/project/qwt/qwt/6.1.3/qwt-6.1.3.zip"
-  URL_MD5 558911df37aee4c0c3049860e967401c
+  URL "http://downloads.sourceforge.net/project/qwt/qwt/6.1.5/qwt-6.1.5.zip"
+  URL_MD5 61a8cae35ab6201d916304ec4a6f06b8
   SOURCE_DIR ${QWT_SB_SRC}
   BINARY_DIR ${QWT_SB_SRC}
   INSTALL_DIR ${SB_INSTALL_PREFIX}
diff --git a/SuperBuild/patches/ITK/itk-3-gcc9-all.diff b/SuperBuild/patches/ITK/itk-3-gcc9-all.diff
deleted file mode 100644
index 100fac60e22f388dc84d478dc5a145031b2f27b9..0000000000000000000000000000000000000000
--- a/SuperBuild/patches/ITK/itk-3-gcc9-all.diff
+++ /dev/null
@@ -1,26 +0,0 @@
-diff --git a/SuperBuild/patches/ITK/itk-3-gcc9-all.diff b/SuperBuild/patches/ITK/itk-3-gcc9-all.diff
-new file mode 100644
-index 0000000000..7ff9131d9d
---- /dev/null
-+++ b/SuperBuild/patches/ITK/itk-3-gcc9-all.diff
-@@ -0,0 +1,20 @@
-+--- InsightToolkit-4.13.1.orig/Modules/ThirdParty/VNL/src/vxl/vcl/vcl_compiler.h	2018-08-09 00:55:23.000000000 +0200
-++++ InsightToolkit-4.13.1/Modules/ThirdParty/VNL/src/vxl/vcl/vcl_compiler.h	2019-11-12 15:49:33.893603688 +0100
-+@@ -97,6 +97,17 @@
-+ #  else
-+ #   define VCL_GCC_80
-+ #  endif
-++# elif (__GNUC__==9)
-++#  define VCL_GCC_9
-++#  if (__GNUC_MINOR__ > 2 )
-++#   define VCL_GCC_92
-++#  elif (__GNUC_MINOR__ > 1 )
-++#   define VCL_GCC_92
-++#  elif (__GNUC_MINOR__ > 0 )
-++#   define VCL_GCC_91
-++#  else
-++#   define VCL_GCC_90
-++#  endif
-+ # else
-+ #  error "Dunno about this gcc"
-+ # endif
diff --git a/SuperBuild/patches/ITK/itk-3-remove-gcc-version-debian-medteam-all.diff b/SuperBuild/patches/ITK/itk-3-remove-gcc-version-debian-medteam-all.diff
new file mode 100644
index 0000000000000000000000000000000000000000..4ee28397620219480fc06559b42dcdba41a9ef6b
--- /dev/null
+++ b/SuperBuild/patches/ITK/itk-3-remove-gcc-version-debian-medteam-all.diff
@@ -0,0 +1,104 @@
+--- a/Modules/ThirdParty/VNL/src/vxl/vcl/vcl_compiler.h
++++ b/Modules/ThirdParty/VNL/src/vxl/vcl/vcl_compiler.h
+@@ -43,85 +43,7 @@
+ #endif
+ 
+ #if defined(__GNUC__) && !defined(__ICC) // icc 8.0 defines __GNUC__
+-# define VCL_GCC
+-# if (__GNUC__ < 4)
+-#  error "forget it."
+-# elif (__GNUC__==4)
+-#  define VCL_GCC_4
+-#  if (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_41
+-#  else
+-#   define VCL_GCC_40
+-#  endif
+-# elif (__GNUC__==5)
+-#  define VCL_GCC_5
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_53
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_52
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_51
+-#  else
+-#   define VCL_GCC_50
+-#  endif
+-# elif (__GNUC__==6)
+-#  define VCL_GCC_6
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_63
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_62
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_61
+-#  else
+-#   define VCL_GCC_60
+-#  endif
+-# elif (__GNUC__==7)
+-#  define VCL_GCC_7
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_73
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_72
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_71
+-#  else
+-#   define VCL_GCC_70
+-#  endif
+-# elif (__GNUC__==8)
+-#  define VCL_GCC_8
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_83
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_82
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_81
+-#  else
+-#   define VCL_GCC_80
+-#  endif
+-# elif (__GNUC__==9)
+-#  define VCL_GCC_9
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_93
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_92
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_91
+-#  else
+-#   define VCL_GCC_90
+-#  endif
+-# elif (__GNUC__==10)
+-#  define VCL_GCC_10
+-#  if (__GNUC_MINOR__ > 2 )
+-#   define VCL_GCC_103
+-#  elif (__GNUC_MINOR__ > 1 )
+-#   define VCL_GCC_102
+-#  elif (__GNUC_MINOR__ > 0 )
+-#   define VCL_GCC_101
+-#  else
+-#   define VCL_GCC_100
+-#  endif
+-# else
+-#  error "Dunno about this gcc"
+-# endif
++# define VCL_GCC_73
+ #endif
+ 
+ #if defined(_WIN32) || defined(WIN32)
+--- a/Modules/ThirdParty/VNL/src/vxl/vcl/tests/test_preprocessor.cxx
++++ b/Modules/ThirdParty/VNL/src/vxl/vcl/tests/test_preprocessor.cxx
+@@ -64,6 +64,12 @@
+   ++minor_count;
+ #endif
+ 
++#ifdef VCL_GCC_73
++  ++compiler_count;
++  ++major_count;
++  ++minor_count;
++#endif
++
+ #ifdef VCL_VC
+   ++compiler_count;
+ #endif
diff --git a/SuperBuild/patches/QT5/qt5-1-jpeg-detection-win.diff b/SuperBuild/patches/QT5/qt5-1-jpeg-detection-win.diff
new file mode 100644
index 0000000000000000000000000000000000000000..b6449ca8678cb816e9abee75b3239508378813f6
--- /dev/null
+++ b/SuperBuild/patches/QT5/qt5-1-jpeg-detection-win.diff
@@ -0,0 +1,13 @@
+--- qt-everywhere-src-5.10.1/qtbase/src/gui/configure.json	2018-02-08 19:24:48.000000000 +0100
++++ QT5/qtbase/src/gui/configure.json	2018-04-10 11:50:15.631526687 +0200
+@@ -278,8 +278,7 @@
+                 "main": "jpeg_create_compress(cinfo);"
+             },
+             "sources": [
+-                { "libs": "-llibjpeg", "condition": "config.msvc" },
+-                { "libs": "-ljpeg", "condition": "!config.msvc" }
++                { "libs": "-ljpeg" }
+             ]
+         },
+         "libpng": {
+
diff --git a/SuperBuild/patches/QT5/qt5-2-png-freetype-detection-all.diff b/SuperBuild/patches/QT5/qt5-2-png-freetype-detection-all.diff
index 25ed6ae8602e71e71803ae8187d92e5f3908f750..3b6d5f0d01d40c49d604ae25157907e876025fa1 100644
--- a/SuperBuild/patches/QT5/qt5-2-png-freetype-detection-all.diff
+++ b/SuperBuild/patches/QT5/qt5-2-png-freetype-detection-all.diff
@@ -1,29 +1,24 @@
-diff -burN  qt-everywhere-src-5.14.1_orig/qtbase/src/gui/configure.json qt-everywhere-src-5.14.1/qtbase/src/gui/configure.json--- qt-everywhere-src-5.14.1_orig/qtbase/src/gui/configure.json   2020-07-27 18:13:35.779000908 +0200
-+++ qt-everywhere-src-5.14.1/qtbase/src/gui/configure.json  2020-07-27 18:18:11.226987484 +0200
-@@ -239,10 +239,10 @@
+--- qt-everywhere-src-5.10.1/qtbase/src/gui/configure.json  2018-02-08 19:24:48.000000000 +0100
++++ QT5/src/QT5/qtbase/src/gui/configure.json   2018-04-10 14:34:05.529668610 +0200
+@@ -158,8 +158,8 @@
+                 ]
              },
-             "headers": "ft2build.h",
              "sources": [
 -                { "type": "pkgConfig", "args": "freetype2" },
-                 { "type": "freetype", "libs": "-lfreetype", "condition": "!config.wasm" },
-                 { "libs": "-s USE_FREETYPE=1", "condition": "config.wasm" },
--                { "libs": "-lfreetype" }
-+                { "libs": "-lfreetype" },
+-                { "type": "freetype", "libs": "-lfreetype" }
++                { "type": "freetype", "libs": "-lfreetype" },
 +                { "type": "pkgConfig", "args": "freetype2" }
-             ],
-             "use": [
-                 { "lib": "zlib", "condition": "features.system-zlib" }
-@@ -397,12 +397,12 @@
+             ]
+         },
+         "fontconfig": {
+@@ -289,9 +289,9 @@
+                 "main": "(void) png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0);"
              },
-             "headers": "png.h",
              "sources": [
 -                { "type": "pkgConfig", "args": "libpng" },
-                 { "libs": "-llibpng16", "condition": "config.msvc" },
++                { "libs": "-lpng", "condition": "!config.msvc" },
                  { "libs": "-llibpng", "condition": "config.msvc" },
-                 { "libs": "-lpng16", "condition": "!config.msvc" },
-                 { "libs": "-lpng", "condition": "!config.msvc" },
--                { "libs": "-s USE_LIBPNG=1", "condition": "config.wasm" }
-+                { "libs": "-s USE_LIBPNG=1", "condition": "config.wasm" },
+-                { "libs": "-lpng", "condition": "!config.msvc" }
 +                { "type": "pkgConfig", "args": "libpng" }
              ],
              "use": [
diff --git a/SuperBuild/patches/QT5/qt5-1-opengl_include_dir-macx.diff b/SuperBuild/patches/QT5/qt5-3-opengl_include_dir-macx.diff
similarity index 100%
rename from SuperBuild/patches/QT5/qt5-1-opengl_include_dir-macx.diff
rename to SuperBuild/patches/QT5/qt5-3-opengl_include_dir-macx.diff
diff --git a/SuperBuild/patches/QT5/qt5-3-undefined-references-linux.diff b/SuperBuild/patches/QT5/qt5-4-undefined-references-linux.diff
similarity index 73%
rename from SuperBuild/patches/QT5/qt5-3-undefined-references-linux.diff
rename to SuperBuild/patches/QT5/qt5-4-undefined-references-linux.diff
index 97b3de7f49974133a2c97f7a3a8ff8db0147e072..2beb06c57b39023aad8dd378303a68f433b8ee16 100644
--- a/SuperBuild/patches/QT5/qt5-3-undefined-references-linux.diff
+++ b/SuperBuild/patches/QT5/qt5-4-undefined-references-linux.diff
@@ -65,3 +65,26 @@ diff -burN  qt-everywhere-src-5.14.1/qttools/src/assistant/assistant/assistant.p
 +LIBS += -lpng
  
  load(qt_app)
+
+--- qt-everywhere-src-5.11.3-orig/qttools/src/assistant/qcollectiongenerator/qcollectiongenerator.pro   2018-11-23 08:41:24.000000000 +0100
++++ qt-everywhere-src-5.11.3/qttools/src/assistant/qcollectiongenerator/qcollectiongenerator.pro    2020-09-04 15:42:48.968480227 +0200
+@@ -1,5 +1,8 @@
+ QT += network help-private
+ TARGET = qcollectiongenerator
++
++LIBS += -lpng
++
+ SOURCES += ../shared/helpgenerator.cpp \
+     main.cpp \
+     ../shared/collectionconfiguration.cpp
+
+--- qt-everywhere-src-5.11.3-orig/qttools/src/assistant/qhelpconverter/qhelpconverter.pro   2018-11-23 08:41:24.000000000 +0100
++++ qt-everywhere-src-5.11.3/qttools/src/assistant/qhelpconverter/qhelpconverter.pro    2020-09-04 15:48:05.003811338 +0200
+@@ -1,5 +1,7 @@
+ QT += help widgets
+ 
++LIBS += -lpng
++
+ SOURCES += conversionwizard.cpp \
+            inputpage.cpp \
+            generalpage.cpp \
diff --git a/SuperBuild/patches/QT5/qt5-5-gcc9-mersenne_twister-all.diff b/SuperBuild/patches/QT5/qt5-5-gcc9-mersenne_twister-all.diff
new file mode 100644
index 0000000000000000000000000000000000000000..f9f1bcfbf464b253d6970300bf6da48fa1159756
--- /dev/null
+++ b/SuperBuild/patches/QT5/qt5-5-gcc9-mersenne_twister-all.diff
@@ -0,0 +1,13 @@
+--- a/qtbase/src/corelib/global/qrandom.cpp
++++ b/qtbase/src/corelib/global/qrandom.cpp
+@@ -218,6 +218,7 @@ struct QRandomGenerator::SystemGenerator
+ #endif // Q_OS_WINRT
+ 
+     static SystemGenerator &self();
++    typedef quint32 result_type;
+     void generate(quint32 *begin, quint32 *end) Q_DECL_NOEXCEPT_EXPR(FillBufferNoexcept);
+ 
+     // For std::mersenne_twister_engine implementations that use something
+-- 
+2.16.3
+
diff --git a/SuperBuild/patches/QWT/qwt-1-releaseBuild-all.diff b/SuperBuild/patches/QWT/qwt-1-releaseBuild-all.diff
index 3d4f6a55954d6fbeeb3a69ba3d66ac4faa7fd9c5..ad34cd24bdfcf46174ac2699e8bfd285ab5c184c 100644
--- a/SuperBuild/patches/QWT/qwt-1-releaseBuild-all.diff
+++ b/SuperBuild/patches/QWT/qwt-1-releaseBuild-all.diff
@@ -8,7 +8,7 @@ diff -burN qwt-6.1.3-orig/qwtbuild.pri qwt-6.1.3/qwtbuild.pri
 -    # On Windows you can't mix release and debug libraries.
 -    # The designer is built in release mode. If you like to use it
 -    # you need a release version. For your own application development you
--    # might need a debug version. 
+-    # might need a debug version.
 -    # Enable debug_and_release + build_all if you want to build both.
  
 -    CONFIG           += debug_and_release