diff --git a/TimeSeries/planet_mosaics.py b/TimeSeries/planet_mosaics.py
index ce8cb236b5915a60e9ad7a92c5dd9be06214ef95..8d0563f6685a7c23b58d53068485691af7c7f843 100644
--- a/TimeSeries/planet_mosaics.py
+++ b/TimeSeries/planet_mosaics.py
@@ -8,6 +8,8 @@ from tqdm import tqdm
 import urllib.request
 from datetime import datetime
 from dateutil.relativedelta import *
+import uuid
+import glob
 
 def fetch(shp, dt, out_fld, api_key):
     ds = ogr.Open(shp)
@@ -80,21 +82,12 @@ def fetch(shp, dt, out_fld, api_key):
 
     return
 
-
-
-
-
-
-
-
-'''
-class PlanetMosaicQuadPipeline:
+class PlanetMosaicPipeline:
     # --- BEGIN SENSOR PROTOTYPE ---
 
     NAME = 'PLANET-MOSAICS'
     REF_TYPE = otb.ImagePixelType_uint16
-    PTRN_dir = '*'
-    PTRN_ref = '*'
+    PTRN_name = 'PlanetMosaic_*.tif'
     FEAT_exp = {
         'B': 'im1b1',
         'G': 'im1b2',
@@ -104,119 +97,83 @@ class PlanetMosaicQuadPipeline:
     }
     NDT = 0
 
+    # ---- END SENSOR PROTOTYPE ----
+
     @classmethod
     def _check(cls,x):
-        return cls.PTRN_dir.split('_')[-1].replace('*', '') in os.path.basename(x)
+        return (cls.PTRN_name.split('*')[0] in os.path.basename(x) and
+                cls.PTRN_name.split('*')[1] in os.path.basename(x))
 
     @classmethod
     def _img_id(cls,x):
         if cls._check(x):
-            if os.path.isdir(x) or os.path.splitext(x)[-1] == '.zip':
-                return '_'.join(os.path.splitext(x)[0].split('_')[-5:])
-            else:
-                return None
+            return os.path.splitext(os.path.basename(x))
         else:
             return None
 
     @classmethod
     def _img_date(cls,x):
         if cls._check(x):
-            if os.path.isdir(x) or os.path.splitext(x)[-1] == '.zip':
-                return os.path.splitext(x)[0].split('_')[-5].split('T')[0]
-            else:
-                return None
+            return os.path.splitext(os.path.basename(x))[0].split('_')[-1]
         else:
             return None
-
-    @classmethod
-    def _tile_id(cls,x):
-        if cls._check(x):
-            if os.path.isdir(x) or os.path.splitext(x)[-1] == '.zip':
-                return os.path.splitext(x)[0].split('_')[-2]
-            else:
-                return None
-        else:
-            return None
-
-    @classmethod
-    def _tile_cloud_percentage(cls, x):
-        if cls._check(x):
-            if os.path.isdir(x):
-                fid = open(glob.glob(os.path.join(x, '*/*/MTD_TL.xml'))[0], 'r')
-                mtd = fid.read()
-            elif os.path.splitext(x)[-1] == '.zip':
-                arch = zipfile.ZipFile(x)
-                fid = [name for name in arch.namelist() if name.endswith('MTD_TL.xml')]
-                mtd = arch.read(fid[0])
-            root = et.fromstring(mtd)
-            f = filter(lambda x: x.tag == "CLOUDY_PIXEL_PERCENTAGE", root.findall('*/*/*'))
-            r = list(f)
-            return float(r[0].text)
-
-    """
-    def _process_mask(self, msks):
-        msk_pipe = [otb.Registry.CreateApplication('Superimpose')]
-        msk_pipe[-1].SetParameterInputImage('inr', self.pipe[-1].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterInputImage('inm', msks[0].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('interpolator', 'nn')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BandMath'))
-        msk_pipe[-1].AddImageToParameterInputImageList('il', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('exp', 'im1b1==0 || im1b1==1 || im1b1==2 || im1b1==7 ||im1b1==3 || im1b1==8 || im1b1==9 || im1b1==10')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BinaryMorphologicalOperation'))
-        msk_pipe[-1].SetParameterInputImage('in', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('filter', 'erode')
-        msk_pipe[-1].SetParameterString('structype', 'ball')
-        msk_pipe[-1].SetParameterString('xradius', '5')
-        msk_pipe[-1].SetParameterString('yradius', '5')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BinaryMorphologicalOperation'))
-        msk_pipe[-1].SetParameterInputImage('in', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('filter', 'dilate')
-        msk_pipe[-1].SetParameterString('structype', 'ball')
-        msk_pipe[-1].SetParameterString('xradius', '20')
-        msk_pipe[-1].SetParameterString('yradius', '20')
-        msk_pipe[-1].Execute()
-        return msk_pipe
-    """
-
-    def _process_mask(self, msks):
-        msk_pipe = [otb.Registry.CreateApplication('BandMath')]
-        msk_pipe[-1].AddImageToParameterInputImageList('il', msks[0].GetParameterOutputImage('out'))
-        # excluding thin cirrus from masking : no im1b1 == 10 below
-        # excluding cloud shadows here (for opening) : no im1b1 == 3 below
-        msk_pipe[-1].SetParameterString('exp', 'im1b1==0 || im1b1==1 || im1b1==8 || im1b1==9')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BandMath'))
-        msk_pipe[-1].AddImageToParameterInputImageList('il', msks[0].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('exp', 'im1b1==2 || im1b1 == 3 || im1b1==7')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BinaryMorphologicalOperation'))
-        msk_pipe[-1].SetParameterInputImage('in', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('filter', 'opening')
-        msk_pipe[-1].SetParameterString('structype', 'ball')
-        msk_pipe[-1].SetParameterString('xradius', '5')
-        msk_pipe[-1].SetParameterString('yradius', '5')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BandMath'))
-        msk_pipe[-1].AddImageToParameterInputImageList('il', msk_pipe[-4].GetParameterOutputImage('out'))
-        msk_pipe[-1].AddImageToParameterInputImageList('il', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('exp', 'im1b1 || im2b1')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('BinaryMorphologicalOperation'))
-        msk_pipe[-1].SetParameterInputImage('in', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('filter', 'dilate')
-        msk_pipe[-1].SetParameterString('structype', 'ball')
-        msk_pipe[-1].SetParameterString('xradius', '20')
-        msk_pipe[-1].SetParameterString('yradius', '20')
-        msk_pipe[-1].Execute()
-        msk_pipe.append(otb.Registry.CreateApplication('Superimpose'))
-        msk_pipe[-1].SetParameterInputImage('inr', self.pipe[-1].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterInputImage('inm', msk_pipe[-2].GetParameterOutputImage('out'))
-        msk_pipe[-1].SetParameterString('interpolator', 'nn')
-        msk_pipe[-1].Execute()
-        return msk_pipe
-
-    # ---- END SENSOR PROTOTYPE ----
-'''
\ No newline at end of file
+    def __init__(self, fld, input_date_interval=None):
+        self.pipe = []
+        self.files = []
+        self.types = []
+        self.out_p = []
+        self.out_idx = []
+
+        self.id = str(uuid.uuid4())
+        self.folder = os.path.abspath(fld)
+        self.image_list = self.parse_folder(self.folder)
+        self.input_dates = [self._img_date(x) for x in self.image_list]
+
+        if input_date_interval is not None:
+            idx = [i for i in range(len(self.input_dates)) if
+                   input_date_interval[0] <= self.input_dates[i] <= input_date_interval[1]]
+            self.image_list = [self.image_list[i] for i in idx]
+            self.input_dates = [self.input_dates[i] for i in idx]
+
+        for img in self.image_list:
+            self.append(to_otb_pipeline(img), os.path.basename(img), self.REF_TYPE, 'out', is_output=True)
+
+    def __del__(self):
+        return
+
+    def parse_folder(self, fld):
+        img_list = [os.path.abspath(x) for x in glob.glob(os.path.join(fld, self.PTRN_dir))]
+        return sorted(img_list, key=lambda x: self._img_id(x))
+
+    def compute_features(self, feat_list=['B', 'G', 'R', 'NIR', 'NDVI']):
+        proc_idx = self.out_idx.copy()
+        self.out_idx = []
+        for k in range(len(proc_idx)):
+            bm = otb.Registry.CreateApplication('BandMathX')
+            expr = []
+            for f in feat_list:
+                expr.append(self.FEAT_exp[f])
+            expr = '{' + ';'.join(expr) + '}'
+            bm.SetParameterString('exp', expr)
+            bm.Execute()
+            fn = self.files[proc_idx[k]].replace('.tif', '_FEAT.tif')
+            self.append(bm, fn, self.REF_TYPE, 'out', is_output=True)
+
+    def write_outputs(self, fld, update_pipe=False, compress=False):
+        out = []
+        proc_idx = self.out_idx.copy()
+        if update_pipe:
+            self.out_idx = []
+        for t in proc_idx:
+            out_file = os.path.join(fld, self.files[t])
+            if compress:
+                out_file += '?gdal:co:compress=deflate'
+            if not os.path.exists(os.path.dirname(out_file)):
+                os.makedirs(os.path.dirname(out_file))
+            self.pipe[t].SetParameterString(self.out_p[t], out_file)
+            self.pipe[t].SetParameterOutputImagePixelType(self.out_p[t], self.types[t])
+            self.pipe[t].ExecuteAndWriteOutput()
+            out.append(out_file)
+            if update_pipe:
+                self.append(to_otb_pipeline(out_file), self.files[t], self.types[t], 'out', is_output=True)
+        return out
diff --git a/TimeSeries/s1base.py b/TimeSeries/s1base.py
index 22f897513d94957e565d28f8030575a267dd464d..3a53d80936f37c59660546bb6e045536a4abf95e 100644
--- a/TimeSeries/s1base.py
+++ b/TimeSeries/s1base.py
@@ -113,7 +113,6 @@ class S1GRDPipeline:
             shutil.rmtree(self.temp_fld)
 
     def parse_folder(self, fld, roi_min_surf=0.0):
-        print(glob.glob(os.path.join(fld, self.PTRN_dir)))
         img_list = [os.path.abspath(x) for x in glob.glob(os.path.join(fld, self.PTRN_dir))
                     if os.path.isdir(x) and self._check_roi(x, self.roi, roi_min_surf)]