diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 50e651b602cbd332f98e6c58403e793b8d2d0624..6c1ca95ce89fb5ca967714b66d0869f6a7f5b2fb 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -12,3 +12,7 @@ OTB_CREATE_APPLICATION(NAME           MeanResample
                        SOURCES        otbMeanResample.cxx
                        LINK_LIBRARIES OTBCommon)
 
+OTB_CREATE_APPLICATION(NAME           ZonalStatistics
+                       SOURCES        otbZonalStatistics.cxx
+                       LINK_LIBRARIES OTBCommon)
+
diff --git a/app/otbZonalStatistics.cxx b/app/otbZonalStatistics.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cfeb84ae794a96e281a6d5ac6d11c7f4f0debd2e
--- /dev/null
+++ b/app/otbZonalStatistics.cxx
@@ -0,0 +1,329 @@
+/*=========================================================================
+
+  Copyright (c) Remi Cresson (IRSTEA). All rights reserved.
+
+
+     This software is distributed WITHOUT ANY WARRANTY; without even
+     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+     PURPOSE.  See the above copyright notices for more information.
+
+=========================================================================*/
+#include "itkFixedArray.h"
+#include "itkObjectFactory.h"
+
+// Elevation handler
+#include "otbWrapperElevationParametersHandler.h"
+#include "otbWrapperApplicationFactory.h"
+
+// Application engine
+#include "otbStandardFilterWatcher.h"
+#include "itkFixedArray.h"
+
+// Filter
+#include "otbVectorDataToLabelImageFilter.h"
+#include "otbVectorDataIntoImageProjectionFilter.h"
+
+// Vnl vector
+#include "vnl/vnl_vector.h"
+
+// itk iterator
+#include "itkImageRegionConstIterator.h"
+
+using namespace std;
+
+namespace otb
+{
+
+namespace Wrapper
+{
+
+class ZonalStatistics : public Application
+{
+public:
+  /** Standard class typedefs. */
+  typedef ZonalStatistics                        Self;
+  typedef Application                   Superclass;
+  typedef itk::SmartPointer<Self>       Pointer;
+  typedef itk::SmartPointer<const Self> ConstPointer;
+
+
+  /* vector data filters */
+  typedef size_t                                                      LabelValueType;
+  typedef otb::Image<LabelValueType, 2>                               LabelImageType;
+  typedef otb::VectorData<double, 2>                                  VectorDataType;
+  typedef otb::VectorDataIntoImageProjectionFilter<VectorDataType,
+      FloatVectorImageType>                                           VectorDataReprojFilterType;
+  typedef otb::VectorDataToLabelImageFilter<VectorDataType,
+      LabelImageType>                                                 RasterizeFilterType;
+  typedef VectorDataType::DataTreeType                                DataTreeType;
+  typedef itk::PreOrderTreeIterator<DataTreeType>                     TreeIteratorType;
+  typedef VectorDataType::DataNodeType                                DataNodeType;
+  typedef DataNodeType::PolygonListPointerType                        PolygonListPointerType;
+
+  /** Statistics */
+  typedef vnl_matrix<double>                                          RealMatrix;
+  typedef vnl_vector<LabelValueType>                                  SizeVector;
+
+  /** Image iterator */
+  typedef itk::ImageRegionConstIterator<LabelImageType>               LabelIteratorType;
+  typedef itk::ImageRegionConstIterator<FloatVectorImageType>         ImageIteratorType;
+
+  /** Standard macro */
+  itkNewMacro(Self);
+  itkTypeMacro(ZonalStatistics, Application);
+
+  void DoInit()
+  {
+
+    SetName("ZonalStatistics");
+    SetDescription("Computes zonal statistics");
+
+    // Documentation
+    SetDocName("ZonalStatistics");
+    SetDocLongDescription("This application zonal statistics");
+    SetDocLimitations("None");
+    SetDocAuthors("Remi Cresson");
+
+    AddDocTag(Tags::Manip);
+
+    // Inputs
+    AddParameter(ParameterType_InputImage,   "in",  "Input Image");
+    AddParameter(ParameterType_InputVectorData, "vec", "Input vector data");
+
+    // No data value
+    AddParameter(ParameterType_Float, "nodata", "No-data value");
+    MandatoryOff("nodata");
+
+    // Output
+    AddParameter(ParameterType_OutputVectorData, "out",   "Output vector data");
+
+    AddRAMParameter();
+
+    // Doc example parameter settings
+    SetDocExampleParameterValue("in", "input.tif");
+    SetDocExampleParameterValue("vec", "terrain.shp");
+    SetDocExampleParameterValue("out", "terain_with_stats.shp");
+
+
+  }
+
+  void DoUpdateParameters()
+  {
+    // Nothing to do here : all parameters are independent
+  }
+
+  // Create a string of the kind "prefix_i"
+  std::string CreateFieldName(std::string prefix, unsigned int i)
+  {
+    std::stringstream ss;
+    ss << prefix << "_" << i;
+    return ss.str();
+  }
+
+  void DoExecute()
+  {
+
+    // Get inputs
+    FloatVectorImageType::Pointer img = GetParameterImage("in");
+    VectorDataType* shp = GetParameterVectorData("vec");
+    const bool has_nodata = HasValue("nodata");
+    FloatVectorImageType::InternalPixelType m_InputNoData = 0;
+    if (has_nodata)
+      {
+      m_InputNoData = GetParameterFloat("nodata");
+      otbAppLogINFO("Using user no-data value: " << m_InputNoData);
+      }
+
+    // Reproject vector data
+    otbAppLogINFO("Reproject vector data");
+    m_VectorDataReprojectionFilter = VectorDataReprojFilterType::New();
+    m_VectorDataReprojectionFilter->SetInputVectorData(shp);
+    m_VectorDataReprojectionFilter->SetInputImage(img);
+    m_VectorDataReprojectionFilter->Update();
+
+    // Add a FID field
+    otbAppLogINFO("Adding internal FID");
+    LabelValueType internalFID = 1;
+    const string internalFIDField = "fid____";
+    vdWithFid = VectorDataType::New();
+    DataNodeType::Pointer root1 = vdWithFid->GetDataTree()->GetRoot()->Get();
+    DataNodeType::Pointer document1 = DataNodeType::New();
+    document1->SetNodeType(otb::DOCUMENT);
+    vdWithFid->GetDataTree()->Add(document1, root1);
+    DataNodeType::Pointer folder1 = DataNodeType::New();
+    folder1->SetNodeType(otb::FOLDER);
+    vdWithFid->GetDataTree()->Add(folder1, document1);
+    vdWithFid->SetProjectionRef(m_VectorDataReprojectionFilter->GetOutput()->GetProjectionRef());
+    TreeIteratorType itVector1(m_VectorDataReprojectionFilter->GetOutput()->GetDataTree());
+    itVector1.GoToBegin();
+    while (!itVector1.IsAtEnd())
+      {
+      if (!itVector1.Get()->IsRoot() && !itVector1.Get()->IsDocument() && !itVector1.Get()->IsFolder())
+        {
+        DataNodeType::Pointer currentGeometry = itVector1.Get();
+        currentGeometry->SetFieldAsInt(internalFIDField, internalFID );
+        vdWithFid->GetDataTree()->Add(currentGeometry, folder1);
+        internalFID++;
+        }
+      ++itVector1;
+      } // next feature
+
+    // Rasterize vector data
+    otbAppLogINFO("Rasterize vector data");
+    m_RasterizeFilter = RasterizeFilterType::New();
+    m_RasterizeFilter->AddVectorData(vdWithFid);
+    m_RasterizeFilter->SetOutputOrigin(img->GetOrigin());
+    m_RasterizeFilter->SetOutputSpacing(img->GetSignedSpacing());
+    m_RasterizeFilter->SetOutputSize(img->GetLargestPossibleRegion().GetSize());
+    m_RasterizeFilter->SetOutputProjectionRef(img->GetProjectionRef());
+    m_RasterizeFilter->SetBurnAttribute(internalFIDField);
+    m_RasterizeFilter->Update();
+
+    // Computing stats
+    otbAppLogINFO("Computing stats");
+
+    // Explicit streaming over the input target image, based on the RAM parameter
+    typedef otb::RAMDrivenStrippedStreamingManager<FloatVectorImageType> StreamingManagerType;
+    StreamingManagerType::Pointer m_StreamingManager = StreamingManagerType::New();
+    m_StreamingManager->SetAvailableRAMInMB(GetParameterInt("ram"));
+    m_StreamingManager->PrepareStreaming(img, img->GetLargestPossibleRegion() );
+
+    // Init. stats containers
+    const LabelValueType N = internalFID;
+    const unsigned int nBands = img->GetNumberOfComponentsPerPixel();
+    RealMatrix minimums(nBands, N, itk::NumericTraits<double>::max());
+    RealMatrix maximums(nBands, N, itk::NumericTraits<double>::min());
+    RealMatrix sum     (nBands, N, 0);
+    RealMatrix sqSum   (nBands, N, 0);
+    RealMatrix means   (nBands, N, 0);
+    RealMatrix stdevs  (nBands, N, 0);
+    SizeVector counts(N,0);
+    if (m_RasterizeFilter->GetOutput()->GetLargestPossibleRegion().GetNumberOfPixels() != img->GetLargestPossibleRegion().GetNumberOfPixels())
+      {
+      otbAppLogFATAL("Rasterized image and input image don't have the same number of pixels");
+      }
+
+    int m_NumberOfDivisions = m_StreamingManager->GetNumberOfSplits();
+    for (int m_CurrentDivision = 0; m_CurrentDivision < m_NumberOfDivisions; m_CurrentDivision++)
+      {
+      std::cout << "region: " << m_CurrentDivision << std::endl;
+      FloatVectorImageType::RegionType streamRegion = m_StreamingManager->GetSplit(m_CurrentDivision);
+      img->SetRequestedRegion(streamRegion);
+      img->PropagateRequestedRegion();
+      img->UpdateOutputData();
+
+      LabelIteratorType it(m_RasterizeFilter->GetOutput(), streamRegion);
+      ImageIteratorType it_in(img, streamRegion);
+      for (it.GoToBegin(), it_in.GoToBegin(); !it.IsAtEnd(); ++it, ++it_in)
+        {
+        const LabelValueType fid = it.Get();
+        const FloatVectorImageType::PixelType pix = it_in.Get();
+        bool valid = true;
+        if (has_nodata)
+          {
+          for (unsigned int band = 0 ; band < nBands ; band++)
+            {
+            const FloatVectorImageType::InternalPixelType val = pix[band];
+            if (val == m_InputNoData)
+              {
+              valid = false;
+              break;
+              }
+            }
+          }
+        if (valid)
+          {
+          for (unsigned int band = 0 ; band < nBands ; band++)
+            {
+            const FloatVectorImageType::InternalPixelType val = pix[band];
+            if (val < minimums[band][fid])
+              minimums[band][fid] = val;
+            if (val > maximums[band][fid])
+              maximums[band][fid] = val;
+            sum[band][fid]+=val;
+            sqSum[band][fid]+=val*val;
+            } // next band
+          counts[fid]++;
+          }
+        } // next pixel
+      }
+
+    // Summarize stats
+    otbAppLogINFO("Summarizing stats");
+    for (LabelValueType fid = 0 ; fid < N ; fid++)
+      {
+      const LabelValueType count = counts[fid];
+      for (unsigned int band = 0 ; band < nBands ; band++)
+        {
+        if (count>0)
+          {
+          means[band][fid] = sum[band][fid] / static_cast<FloatVectorImageType::InternalPixelType>(count);
+
+          // Unbiased estimate
+          FloatVectorImageType::InternalPixelType variance = 0;
+          if (count > 1)
+            {
+            variance =
+                (sqSum[band][fid] - (sum[band][fid]*sum[band][fid] / static_cast<FloatVectorImageType::InternalPixelType>(count) ) ) /
+                (static_cast<FloatVectorImageType::InternalPixelType>(count) - 1);
+            if (variance > 0)
+              {
+              stdevs[band][fid] = vcl_sqrt(variance);
+              }
+            }
+          }
+        }
+      }
+
+    // Add a statistics fields
+    otbAppLogINFO("Writing stats in output vector data");
+    internalFID = 1;
+    vdWithStats = VectorDataType::New();
+    DataNodeType::Pointer root = vdWithStats->GetDataTree()->GetRoot()->Get();
+    DataNodeType::Pointer document = DataNodeType::New();
+    document->SetNodeType(otb::DOCUMENT);
+    vdWithStats->GetDataTree()->Add(document, root);
+    DataNodeType::Pointer folder = DataNodeType::New();
+    folder->SetNodeType(otb::FOLDER);
+    vdWithStats->GetDataTree()->Add(folder, document);
+    vdWithStats->SetProjectionRef(m_VectorDataReprojectionFilter->GetOutput()->GetProjectionRef());
+    TreeIteratorType itVector(m_VectorDataReprojectionFilter->GetOutput()->GetDataTree());
+    itVector.GoToBegin();
+    while (!itVector.IsAtEnd())
+      {
+      if (!itVector.Get()->IsRoot() && !itVector.Get()->IsDocument() && !itVector.Get()->IsFolder())
+        {
+        DataNodeType::Pointer currentGeometry = itVector.Get();
+        currentGeometry->SetFieldAsInt("fid", internalFID );
+        for (unsigned int band = 0 ; band < nBands ; band++)
+          {
+          currentGeometry->SetFieldAsDouble(CreateFieldName("mean",  band), means   [band][internalFID] );
+          currentGeometry->SetFieldAsDouble(CreateFieldName("stdev", band), stdevs  [band][internalFID] );
+          currentGeometry->SetFieldAsDouble(CreateFieldName("min",   band), minimums[band][internalFID] );
+          currentGeometry->SetFieldAsDouble(CreateFieldName("max",   band), maximums[band][internalFID] );
+//          currentGeometry->SetFieldAsDouble(CreateFieldName("sum",   band), sum     [band][internalFID] );
+//          currentGeometry->SetFieldAsDouble(CreateFieldName("sqSum", band), sqSum   [band][internalFID] );
+          }
+        currentGeometry->SetFieldAsDouble("count", static_cast<double>(counts[internalFID]) );
+
+        vdWithStats->GetDataTree()->Add(currentGeometry, folder);
+        internalFID++;
+        }
+      ++itVector;
+      } // next feature
+
+
+    SetParameterOutputVectorData("out", vdWithStats);
+
+  }
+
+  VectorDataType::Pointer vdWithFid;
+  VectorDataType::Pointer vdWithStats;
+  VectorDataReprojFilterType::Pointer m_VectorDataReprojectionFilter;
+  RasterizeFilterType::Pointer m_RasterizeFilter;
+
+};
+}
+}
+
+OTB_APPLICATION_EXPORT( otb::Wrapper::ZonalStatistics )