diff --git a/test/test_stackedbandinput.py b/test/test_stackedbandinput.py
index ab8d1c54601997e0e65eec1f827ce91121d1d657..c40cd5a29f30afeadfa46addd9c4a007e9dde1d2 100644
--- a/test/test_stackedbandinput.py
+++ b/test/test_stackedbandinput.py
@@ -32,21 +32,86 @@ QGIS_APP = initQgisApplication(qgisResourceDir=resourceDir)
 class testclassDialogTest(unittest.TestCase):
     """Test rerources work."""
 
+    @classmethod
+    def setUpClass(cls):
+
+        cls.srcDir = r'F:\Temp\EOTSV_Dev\DF'
+        cls.stackFiles = file_search(cls.srcDir, '*.tif')
+
     def setUp(self):
         """Runs before each test."""
         pass
-
     def tearDown(self):
         """Runs after each test."""
         pass
 
+    def createTestDatasets(self):
+
+        vsiDir = '/vsimem/tmp'
+
+        ns = 50
+        nl = 100
+
+        r1 = np.arange('2000-01-01', '2001-06-14', step=np.timedelta64(16, 'D'), dtype=np.datetime64)
+        r2 = np.arange('2000-01-01', '2001-06-14', step=np.timedelta64(8, 'D'), dtype=np.datetime64)
+        drv = gdal.GetDriverByName('ENVI')
+        assert isinstance(drv, gdal.Driver)
+        for i, r in enumerate([r1, r2]):
+            p = '{}stack{}.bsq'.format(vsiDir, i+1)
+            p.
+
+        datasets = []
+
+        pass
+
+    def test_outputmodel(self):
+
+        m = OutputImageModel()
+        m.setOutputDir('/vsimem/dub')
+        m.setOutputPrefix('myPrefix')
+
+        stackInfos = [InputStackInfo(f) for f in self.stackFiles]
+        m.setMultiStackSources(stackInfos)
+
+        self.assertTrue(len(m) > 0)
+
+
+        outInfo = m.mOutputImages[0]
+        self.assertIsInstance(outInfo, OutputVRTDescription)
+
+        xml = m.vrtXML(outInfo)
+        self.assertIsInstance(xml, str)
+        eTree = m.vrtXML(outInfo, asElementTree=True)
+        self.assertIsInstance(eTree, ElementTree.ElementTree)
+
+
+
+
+
+    def test_dateparsing(self):
+
+        dsDates = gdal.OpenEx(self.stackFiles[1], allowed_drivers=['ENVI'])
+
+        #dsDates = gdal.Open(self.stackFiles[1])
+        dates = datesFromDataset(dsDates)
+        self.assertEqual(len(dates), dsDates.RasterCount)
+
+
+        dsNoDates = gdal.OpenEx(self.stackFiles[0], allowed_drivers=['ENVI'])
+        dates = datesFromDataset(dsNoDates)
+        self.assertEqual(len(dates), 0)
+
+        s = ""
 
     def test_dialog(self):
         d = StackedBandInputDialog()
-        d.addSources([Img_2014_05_07_LC82270652014127LGN00_BOA, Img_2014_06_16_LE72270652014167CUB00_BOA])
+        d.addSources(self.stackFiles)
         d.show()
 
         QGIS_APP.exec_()
         pass
+
+
+
 if __name__ == "__main__":
     unittest.main()
diff --git a/timeseriesviewer/dateparser.py b/timeseriesviewer/dateparser.py
index 110de79f58a5090da1a0c5ea8c370aa3b068371c..b55da28783e1d7048d8da4d82f193035ff736db5 100644
--- a/timeseriesviewer/dateparser.py
+++ b/timeseriesviewer/dateparser.py
@@ -1,9 +1,10 @@
 
-import os, re, logging
+import os, re, logging, datetime
 from osgeo import gdal
 import numpy as np
 from qgis import *
-logger = logging.getLogger(__file__)
+from qgis.PyQt.QtCore import QDate
+
 
 #regular expression. compile them only once
 
@@ -19,6 +20,7 @@ regYYYYMMDD = re.compile(r'(?P<year>(19|20)\d\d)(?P<hyphen>-?)(?P<month>1[0-2]|0
 regMissingHypen = re.compile(r'^\d{8}')
 regYYYYMM = re.compile(r'([0-9]{4})-(1[0-2]|0[1-9])')
 regYYYYDOY = re.compile(r'(?P<year>(19|20)\d\d)-?(?P<day>36[0-6]|3[0-5][0-9]|[12][0-9]{2}|0[1-9][0-9]|00[1-9])')
+regDecimalYear = re.compile(r'(?P<year>(19|20)\d\d)\.(?P<datefraction>\d\d\d)')
 
 def matchOrNone(regex, text):
     match = regex.search(text)
@@ -27,7 +29,52 @@ def matchOrNone(regex, text):
     else:
         return None
 
-def extractDateTimeGroup(text):
+def dateDOY(date):
+    if isinstance(date, np.datetime64):
+        date = date.astype(datetime.date)
+    return date.timetuple().tm_yday
+
+def daysPerYear(year):
+
+    if isinstance(year, np.datetime64):
+        year = year.astype(datetime.date)
+    if isinstance(year, datetime.date):
+        year = year.timetuple().tm_year
+
+    return dateDOY(datetime.date(year=year, month=12, day=31))
+
+def num2date(n, dt64=True, qDate=False):
+    n = float(n)
+    if n < 1:
+        n += 1
+
+    year = int(n)
+    fraction = n - year
+    yearDuration = daysPerYear(year)
+    yearElapsed = fraction * yearDuration
+
+    import math
+    doy = round(yearElapsed)
+    if doy < 1:
+        doy = 1
+    try:
+        date = datetime.date(year, 1, 1) + datetime.timedelta(days=doy-1)
+    except:
+        s = ""
+    if qDate:
+        return QDate(date.year, date.month, date.day)
+    if dt64:
+        return np.datetime64(date)
+    else:
+        return date
+
+
+def extractDateTimeGroup(text:str)->np.datetime64:
+    """
+    Extracts a date-time-group from a text string
+    :param text: a string
+    :return: numpy.datetime64 in case of success, or None
+    """
     match = regISODate1.search(text)
     if match:
         matchedText = match.group()
@@ -47,6 +94,14 @@ def extractDateTimeGroup(text):
     if match:
         return np.datetime64(match.group())
 
+    match = regDecimalYear.search(text)
+    if match:
+        year = float(match.group('year'))
+        df = float(match.group('datefraction'))
+        num = match.group()
+        return num2date(num)
+
+
     return None
 
 def datetime64FromYYYYMMDD(yyyymmdd):
diff --git a/timeseriesviewer/stackedbandinput.py b/timeseriesviewer/stackedbandinput.py
index 835e255803884477bd1949f0e9fc0932433c464b..95e17bdb4f9de5f9eb4df55d084331f4bf1535ba 100644
--- a/timeseriesviewer/stackedbandinput.py
+++ b/timeseriesviewer/stackedbandinput.py
@@ -21,6 +21,7 @@
 """
 
 import os, re, tempfile, pickle, copy, shutil, locale, uuid, csv, io
+from xml.etree import ElementTree
 from collections import OrderedDict
 from qgis.core import *
 from qgis.gui import *
@@ -36,22 +37,77 @@ from pyqtgraph.graphicsItems.PlotItem import PlotItem
 import pyqtgraph.functions as fn
 import numpy as np
 from osgeo import gdal, gdal_array
-
+import numpy as np
 from timeseriesviewer.utils import *
-#from timeseriesviewer.virtualrasters import *
+from timeseriesviewer.virtualrasters import *
 from timeseriesviewer.models import *
+from timeseriesviewer.dateparser import *
 from timeseriesviewer.plotstyling import PlotStyle, PlotStyleDialog, MARKERSYMBOLS2QGIS_SYMBOLS
 import timeseriesviewer.mimedata as mimedata
 
+
+def datesFromDataset(dataset:gdal.Dataset)->list:
+
+    nb = dataset.RasterCount
+
+    def checkDates(dateList):
+        if not len(dateList) == nb:
+            return False
+        for d in dateList:
+            if not isinstance(d, np.datetime64):
+                return False
+        return True
+
+    searchedKeys = []
+    searchedKeys.append(re.compile('aquisition[ ]*dates$', re.I))
+    searchedKeys.append(re.compile('observation[ ]*dates$', re.I))
+    searchedKeys.append(re.compile('wavelength$', re.I))
+
+    #1. Check Metadata
+    for domain in dataset.GetMetadataDomainList():
+        domainData = dataset.GetMetadata_Dict(domain)
+        assert isinstance(domainData, dict)
+
+        for key, values in domainData.items():
+            for regex in searchedKeys:
+                if regex.search(key.strip()):
+                    values = re.sub('[{}]', '', values)
+                    values = values.split(',')
+                    dateValues = [extractDateTimeGroup(t) for t in values]
+                    if checkDates(dateValues):
+                        return dateValues
+
+
+    #2. Check Band Names
+    bandDates = [extractDateTimeGroup(dataset.GetRasterBand(b+1).GetDescription()) for b in range(nb)]
+    bandDates = [b for b in bandDates if isinstance(b, np.datetime64)]
+    if checkDates(bandDates):
+        return bandDates
+
+    return []
+
 class InputStackInfo(object):
 
     def __init__(self, dataset):
-        if not isinstance(dataset, gdal.Dataset):
-            dataset = gdal.Open(dataset)
+        if isinstance(dataset, str):
+            #test ENVI header first
+            basename = os.path.splitext(dataset)[0]
+
+            if os.path.isfile(basename+'.hdr'):
+                ds = gdal.OpenEx(dataset, allowed_drivers=['ENVI'])
+            if not isinstance(ds, gdal.Dataset):
+                ds = gdal.Open(dataset)
+            if not isinstance(ds, gdal.Dataset):
+                raise Exception('Unable to open {}'.format(dataset))
+
+            dataset = ds
+            del ds
+
         assert isinstance(dataset, gdal.Dataset)
 
         self.mMetadataDomains = dataset.GetMetadataDomainList()
         self.mMetaData = OrderedDict()
+
         for domain in self.mMetadataDomains:
             self.mMetaData[domain] = dataset.GetMetadata_Dict(domain)
 
@@ -64,11 +120,58 @@ class InputStackInfo(object):
 
         self.path = dataset.GetFileList()[0]
 
-        self.bandName = os.path.splitext(os.path.basename(self.path))[0]
+        self.outputBandName = os.path.splitext(os.path.basename(self.path))[0]
+
+
+        self.bandnames = []
+        self.nodatavalues = []
+        for b in range(self.nb):
+            band = dataset.GetRasterBand(b+1)
+            assert isinstance(band, gdal.Band)
+            self.bandnames.append(band.GetDescription())
+            self.nodatavalues.append(band.GetNoDataValue())
+
+
+        self.mDates = datesFromDataset(dataset)
+
+    def __len__(self):
+        return len(self.mDates)
+
+    def dates(self)->list:
+        """Returns a list of dates"""
+        return self.mDates
+
 
     def structure(self):
         return (self.ns, self.nl, self.nb, self.gt, self.wkt)
 
+    def wavelength(self):
+        return self.mMetaData[''].get('wavelength')
+
+    def setWavelength(self, wl):
+        self.mMetaData['']['wavelength'] = wl
+
+
+class OutputVRTDescription(object):
+    """
+    Descrbies an output VRT
+    """
+
+    def __init__(self, path:str, date:np.datetime64):
+        super(OutputVRTDescription, self).__init__()
+        self.mPath = path
+        self.mDate = date
+
+
+    def setPath(self, path:str):
+        self.mPath = path
+
+    def xml(self):
+        """
+        Create the VRT XML
+        :return:
+        """
+
 class InputStackTableModel(QAbstractTableModel):
 
 
@@ -78,16 +181,33 @@ class InputStackTableModel(QAbstractTableModel):
         super(InputStackTableModel, self).__init__(parent)
         self.mStackImages = []
 
-        self.cnFile = 'File'
-        self.cn_fileproperties = 'Properties'
-        self.cn_ns = 'Samples'
-        self.cn_nl = 'Lines'
-        self.cn_nb = 'Bands'
-        self.cb_name = 'Band Name'
+        self.cn_source = 'Source'
+        self.cn_dates = 'Dates'
+        self.cn_crs = 'GT + CRS'
+        self.cn_ns = 'ns'
+        self.cn_nl = 'nl'
+        self.cn_nb = 'nb'
+        self.cn_name = 'Band Name'
         self.cn_wl = 'Wavelength'
-        self.mColumnNames = [self.cnFile, self.cn_fileproperties, self.cn_wl]
+        self.mColumnNames = [self.cn_source, self.cn_dates, self.cn_name, self.cn_wl, self.cn_ns, self.cn_nl, self.cn_nb, self.cn_crs]
+
+        self.mColumnTooltips = {}
+        self.mColumnTooltips[self.cn_source] = 'Stack source uri / file path'
+        self.mColumnTooltips[self.cn_crs] = 'Geo-Transformation + Coordinate Reference System'
+        self.mColumnTooltips[self.cn_ns] = 'Number of samples / pixel in horizontal direction'
+        self.mColumnTooltips[self.cn_nl] = 'Number of lines / pixel in vertical direction'
+        self.mColumnTooltips[self.cn_nb] = 'Number of bands'
+        self.mColumnTooltips[self.cn_name] = 'Prefix of band name in output image'
+        self.mColumnTooltips[self.cn_wl] = 'Wavelength in output image'
+        self.mColumnTooltips[self.cn_dates] = 'Identified dates'
+
+    def __len__(self):
+        return len(self.mStackImages)
+
+    def __iter__(self):
+        return iter(self.mStackImages)
 
-    def columnName(self, i):
+    def columnName(self, i) -> str:
         if isinstance(i, QModelIndex):
             i = i.column()
         return self.mColumnNames[i]
@@ -97,7 +217,7 @@ class InputStackTableModel(QAbstractTableModel):
             columnName = self.columnName(index)
             flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
             if columnName in [self.cn_name, self.cn_wl]: #allow check state
-                flags = flags | Qt.ItemIsUserCheckable
+                flags = flags | Qt.ItemIsEditable
 
             return flags
             #return item.qt_flags(index.column())
@@ -106,8 +226,12 @@ class InputStackTableModel(QAbstractTableModel):
     def headerData(self, col, orientation, role):
         if Qt is None:
             return None
-        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
-            return self.mColumnNames[col]
+        if orientation == Qt.Horizontal:
+            cname = self.mColumnNames[col]
+            if role == Qt.DisplayRole:
+                return cname
+            elif role == Qt.ToolTipRole:
+                return self.mColumnTooltips.get(cname)
         elif orientation == Qt.Vertical and role == Qt.DisplayRole:
             return col
         return None
@@ -127,14 +251,7 @@ class InputStackTableModel(QAbstractTableModel):
         if not isinstance(paths, list):
             paths = [paths]
 
-        infos = []
-        for p in paths:
-            assert isinstance(p, str)
-            ds = gdal.Open(p)
-            if isinstance(ds, gdal.Dataset):
-                info = InputStackInfo(ds)
-                infos.append(info)
-
+        infos = [InputStackInfo(p) for p in paths]
         if len(infos) > 0:
 
             self.beginInsertRows(QModelIndex(), i, i+len(infos)-1)
@@ -142,8 +259,19 @@ class InputStackTableModel(QAbstractTableModel):
                 self.mStackImages.insert(i+j, info)
             self.endInsertRows()
 
+    def removeSources(self, stackInfos:list):
+
+        for stackInfo in stackInfos:
+            assert stackInfo in self.mStackImages
+
+        for stackInfo in stackInfos:
+            assert isinstance(stackInfo, InputStackInfo)
 
+            idx = self.info2index(stackInfo)
 
+            self.beginRemoveRows(QModelIndex(), idx.row(), idx.row())
+            self.mStackImages.remove(stackInfo)
+            self.endRemoveRows()
 
     def isValid(self):
         l = len(self.mStackImages)
@@ -155,45 +283,313 @@ class InputStackTableModel(QAbstractTableModel):
         #all input stacks need to have the same characteristic
         for stackInfo in self.mStackImages[1:]:
             assert isinstance(stackInfo, InputStackInfo)
+            if not ref.dates() == stackInfo.dates():
+                return False
             if not ref.structure() == stackInfo.structure():
                 return False
         return True
 
 
-    def data(self, index: QModelIndex, role: int):
+    def index2info(self, index:QModelIndex) -> InputStackInfo:
+        return self.mStackImages[index.row()]
 
+    def info2index(self, info:InputStackInfo) -> QModelIndex:
+        r = self.mStackImages.index(info)
+        return self.createIndex(r,0, info)
 
+    def data(self, index: QModelIndex, role: int):
         if not index.isValid():
             return None
 
-        info = self.mStackImages[index.column()]
+        info = self.mStackImages[index.row()]
         assert isinstance(info, InputStackInfo)
         cname = self.columnName(index)
-        if role == Qt.DisplayRole:
-            if cname == self.cnFile:
+
+        if role in [Qt.DisplayRole, Qt.ToolTipRole]:
+            if cname == self.cn_source:
                 return info.path
-            elif cname == self.cn_fileproperties:
-                return str(info.structure())
+            if cname == self.cn_dates:
+                dates = info.dates()
+                if role == Qt.DisplayRole:
+                    return len(dates)
+                if role == Qt.ToolTipRole:
+                    if len(dates) == 0:
+                        return 'No dates identified. Can not use this image as input'
+                    else:
+                        if len(dates) > 11:
+                            dates = dates[0:10] + ['...']
+                        return '\n'.join([str(d) for d in dates])
+
+
+            if cname == self.cn_ns:
+                return info.ns
+            if cname == self.cn_nl:
+                return info.nl
+            if cname == self.cn_nb:
+                return info.nb
+            if cname == self.cn_crs:
+                return '{} {}'.format(info.gt, info.wkt)
             elif cname == self.cn_wl:
-                return str(info.structure())
-            elif cname == self.cb_name:
-                return info.bandName
+                return info.mMetaData[''].get('wavelength')
+            elif cname == self.cn_name:
+                return info.outputBandName
+
+        if role == Qt.EditRole:
+            if cname == self.cn_wl:
+                return info.mMetaData[''].get('wavelength')
+            elif cname == self.cn_name:
+                return info.outputBandName
 
+        if role == Qt.BackgroundColorRole:
+            if cname in [self.cn_name, self.cn_wl]:
+                return QColor('yellow')
 
         return None
 
+    def setData(self, index: QModelIndex, value, role: int):
+
+        if not index.isValid():
+            return None
+
+        info = self.index2info(index)
+        cname = self.columnName(index)
+
+        changed = False
+        if role == Qt.EditRole:
+            if cname == self.cn_name:
+                info.outputBandName = value
+                changed = True
+            elif cname == self.cn_wl:
+                info.setWavelength(value)
+                changed = True
+        if changed:
+            self.dataChanged.emit(index, index)
+        return changed
 
 class OutputImageModel(QAbstractTableModel):
 
     def __init__(self, parent=None):
-        pass
+        super(OutputImageModel, self).__init__(parent)
 
+        self.cn_uri = 'Path'
+        self.cn_nb = 'nb'
+        self.cn_date = 'Date'
+        self.mOutputImages = []
 
+        self.mColumnNames = [self.cn_date, self.cn_nb, self.cn_uri]
+        self.mColumnTooltips = {}
+        self.mColumnTooltips[self.cn_nb] = 'Number of bands'
+        self.mColumnTooltips[self.cn_uri] = 'Output location'
+        self.masterVRT_DateLookup = {}
+        self.masterVRT_SourceBandTemplates = {}
+        self.masterVRT_InputStacks = None
+        self.masterVRT_XML = None
+        self.masterVRT_nb = None
+        self.mOutputDir = '/vsimem/'
+        self.mOutputPrefix = 'date'
 
 
-    def outputVRTs(self):
 
-        pass
+    def headerData(self, col, orientation, role):
+        if Qt is None:
+            return None
+        if orientation == Qt.Horizontal:
+            cname = self.mColumnNames[col]
+            if role == Qt.DisplayRole:
+                return cname
+            elif role == Qt.ToolTipRole:
+                return self.mColumnTooltips.get(cname)
+        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
+            return col
+        return None
+
+    def createVRTUri(self, date:np.datetime64):
+
+        path = os.path.join(self.mOutputDir, self.mOutputPrefix)
+        path = '{}{}.vrt'.format(path, date)
+
+        return path
+
+    def clearOutputs(self):
+        self.beginRemoveRows(QModelIndex(), 0, self.rowCount() - 1)
+        self.mOutputImages = []
+        self.endRemoveRows()
+
+    def setMultiStackSources(self, listOfInputStacks:list, nextBand:int=1, nextDate:int=1):
+
+        self.clearOutputs()
+        if len(listOfInputStacks) == 0:
+            return
+        if listOfInputStacks is None or len(listOfInputStacks) == 0:
+            return
+
+        for s in listOfInputStacks:
+            assert isinstance(s, InputStackInfo)
+
+        listOfInputStacks = [s for s in listOfInputStacks if len(s) > 0]
+        numberOfOutputVRTBands = len(listOfInputStacks)
+        self.masterVRT_DateLookup.clear()
+        self.masterVRT_InputStacks = listOfInputStacks
+        self.masterVRT_SourceBandTemplates.clear()
+        dates = set()
+        for s in listOfInputStacks:
+            for d in s.dates():
+                dates.add(d)
+        dates = sorted(list(dates))
+
+        #create a LUT to get the stack indices for a related date (not each stack might contain a band for each date)
+        for d in dates:
+            self.masterVRT_DateLookup[d] = []
+        for stackIndex, s in enumerate(listOfInputStacks):
+            for bandIndex, date in enumerate(s.dates()):
+                self.masterVRT_DateLookup[date].append((stackIndex, bandIndex))
+
+        #create VRT Template XML
+        VRT = VRTRaster()
+        wavelength = []
+        for stackIndex, stack in enumerate(listOfInputStacks):
+            assert isinstance(stack, InputStackInfo)
+            vrtBand = VRTRasterBand()
+            vrtBand.setName(stack.outputBandName)
+            vrtSrc = VRTRasterInputSourceBand(stack.path, 0)
+            vrtBand.addSource(vrtSrc)
+            wavelength.append(stack.wavelength())
+            VRT.addVirtualBand(vrtBand)
+
+        pathVSITmp = '/vsimem/temp.vrt'
+        dsVRT = VRT.saveVRT(pathVSITmp)
+        dsVRT.SetMetadataItem('acquisition date', 'XML_REPLACE_DATE')
+
+        if None not in wavelength:
+            dsVRT.SetMetadataItem('wavelength', ','.join(str(wl) for wl in wavelength))
+            dsVRT.SetMetadataItem('wavelength units', 'Nanometers')
+
+        dsVRT.FlushCache()
+        drv = dsVRT.GetDriver()
+        masterVRT_XML = read_vsimem(pathVSITmp).decode('utf-8')
+        drv.Delete(pathVSITmp)
+        outputVRTs = []
+
+
+        eTree = ElementTree.fromstring(masterVRT_XML)
+        for iBand, elemBand in enumerate(eTree.findall('VRTRasterBand')):
+            sourceElements  = elemBand.findall('ComplexSource') + elemBand.findall('SimpleSource')
+            assert len(sourceElements) == 1
+            self.masterVRT_SourceBandTemplates[iBand] = copy.deepcopy(sourceElements[0])
+            elemBand.remove(sourceElements[0])
+
+        for date in dates:
+            assert isinstance(date, np.datetime64)
+            path = self.createVRTUri(date)
+            outputDescription = OutputVRTDescription(path, date)
+            outputVRTs.append(outputDescription)
+
+        self.masterVRT_XML = eTree
+
+
+        self.beginInsertRows(QModelIndex(), 0, len(outputVRTs)-1)
+        self.mOutputImages = outputVRTs[:]
+        self.endInsertRows()
+
+    def setOutputDir(self, path:str):
+        self.mOutputDir = path
+        self.updateOutputURIs()
+
+    def setOutputPrefix(self, basename:str):
+        self.mOutputPrefix = basename
+        self.updateOutputURIs()
+
+    def updateOutputURIs(self):
+        c = self.mColumnNames.index(self.cn_uri)
+        ul = self.createIndex(0, c)
+        lr = self.createIndex(self.rowCount()-1, c)
+
+        for outputVRT in self:
+            assert isinstance(outputVRT, OutputVRTDescription)
+            outputVRT.setPath(self.createVRTUri(outputVRT.mDate))
+        self.dataChanged.emit(ul, lr)
+
+
+    def __len__(self):
+        return len(self.mOutputImages)
+
+    def __iter__(self):
+        return iter(self.mOutputImages)
+
+    def rowCount(self, parent=None) -> int:
+        return len(self.mOutputImages)
+
+    def columnCount(self, parent=None) -> int:
+        return len(self.mColumnNames)
+
+    def columnName(self, i) -> str:
+        if isinstance(i, QModelIndex):
+            i = i.column()
+        return self.mColumnNames[i]
+
+    def columnIndex(self, columnName:str)-> QModelIndex:
+        c = self.mColumnNames.index(columnName)
+        return self.createIndex(0, c)
+
+    def index2vrt(self, index:QModelIndex) -> OutputVRTDescription:
+        return self.mOutputImages[index.row()]
+
+    def vrt2index(self, vrt:OutputVRTDescription) -> QModelIndex:
+        i = self.mOutputImages[vrt]
+        return self.createIndex(i, 0, vrt)
+
+    def data(self, index: QModelIndex, role: int):
+
+        if not index.isValid():
+            return None
+
+        cname = self.columnName(index)
+        vrt = self.index2vrt(index)
+        if role in [Qt.DisplayRole, Qt.ToolTipRole]:
+            if cname == self.cn_uri:
+                return vrt.mPath
+            if cname == self.cn_nb:
+                return self.masterVRT_nb
+            if cname == self.cn_date:
+                return str(vrt.mDate)
+
+    def vrtXML(self, outputDefinition:OutputVRTDescription, asElementTree=False) -> str:
+        """
+        Create the VRT XML related to an outputDefinition
+        :param outputDefinition:
+        :return: str
+        """
+
+        # re.search(tmpXml, '<MDI key='>')
+
+        # xml = copy.deepcopy(eTree)
+        if self.masterVRT_XML is None:
+            return None
+        #xmlTree = ElementTree.fromstring(self.masterVRT_XML)
+        xmlTree = copy.deepcopy(self.masterVRT_XML)
+
+        # set metadata
+        for elem in xmlTree.findall('Metadata/MDI'):
+            if elem.attrib['key'] == 'acquisition date':
+                elem.text = str(outputDefinition.mDate)
+
+        # insert required rasterbands
+        requiredBands = self.masterVRT_DateLookup[outputDefinition.mDate]
+
+        xmlVRTBands = xmlTree.findall('VRTRasterBand')
+
+        for t in requiredBands:
+            stackIndex, stackBandIndex = t
+
+            stackSourceXMLTemplate = copy.deepcopy(self.masterVRT_SourceBandTemplates[stackIndex])
+            stackSourceXMLTemplate.find('SourceBand').text = str(stackBandIndex+1)
+            xmlVRTBands[stackIndex].append(stackSourceXMLTemplate)
+
+        if asElementTree:
+            return xmlTree
+        else:
+            return ElementTree.tostring(xmlTree).decode('utf-8')
+
 
 
 
@@ -207,13 +603,31 @@ class StackedBandInputDialog(QDialog, loadUI('stackedinputdatadialog.ui')):
 
 
         self.tableModelInputStacks = InputStackTableModel()
+        self.tableModelInputStacks.rowsInserted.connect(self.updateOutputs)
+        self.tableModelInputStacks.dataChanged.connect(self.updateOutputs)
+        self.tableModelInputStacks.rowsRemoved.connect(self.updateOutputs)
         self.tableViewSourceStacks.setModel(self.tableModelInputStacks)
+
+        self.tableModelOutputImages = OutputImageModel()
+        self.tableViewOutputImages.setModel(self.tableModelOutputImages)
+
+
+        self.tbFilePrefix.textChanged.connect(self.tableModelOutputImages.setOutputPrefix)
+        self.tbFilePrefix.setText('img')
         sm = self.tableViewSourceStacks.selectionModel()
         assert isinstance(sm, QItemSelectionModel)
         sm.selectionChanged.connect(self.onSourceStackSelectionChanged)
+        self.onSourceStackSelectionChanged([],[])
 
         self.initActions()
 
+    def updateOutputs(self, *args):
+
+        self.tableModelOutputImages.clearOutputs()
+        inputStacks = self.tableModelInputStacks.mStackImages
+        self.tableModelOutputImages.setMultiStackSources(inputStacks)
+
+
     def initActions(self):
 
         self.actionAddSourceStack.triggered.connect(self.onAddSource)
@@ -230,10 +644,17 @@ class StackedBandInputDialog(QDialog, loadUI('stackedinputdatadialog.ui')):
         self.tableModelInputStacks.insertSources(paths)
 
     def onRemoveSources(self, *args):
-        pass
+
+        model = self.tableViewSourceStacks.selectionModel()
+        assert isinstance(model, QItemSelectionModel)
+
+        infos = [self.tableModelInputStacks.index2info(idx) for idx in model.selectedRows()]
+        self.tableModelInputStacks.removeSources(infos)
+
     def onSourceStackSelectionChanged(self, selected, deselected):
 
-        pass
+        self.actionRemoveSourceStack.setEnabled(len(selected) > 0)
+
 
 
 
diff --git a/timeseriesviewer/ui/stackedinputdatadialog.ui b/timeseriesviewer/ui/stackedinputdatadialog.ui
index 4d82824559448e51ec66022c120bffb708abd1d0..789ce1155b3c6d3f9c8bce3aa2d1f8e61d5526db 100644
--- a/timeseriesviewer/ui/stackedinputdatadialog.ui
+++ b/timeseriesviewer/ui/stackedinputdatadialog.ui
@@ -108,77 +108,6 @@
          <item>
           <widget class="QTableView" name="tableViewSourceStacks"/>
          </item>
-         <item>
-          <layout class="QHBoxLayout" name="horizontalLayout_3">
-           <property name="topMargin">
-            <number>0</number>
-           </property>
-           <item>
-            <widget class="QLabel" name="label_3">
-             <property name="text">
-              <string>Next Date</string>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <widget class="QSpinBox" name="spNextDTG">
-             <property name="minimum">
-              <number>1</number>
-             </property>
-             <property name="maximum">
-              <number>999999</number>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <widget class="QLabel" name="sbNextBand">
-             <property name="text">
-              <string>Next Band</string>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <widget class="QSpinBox" name="spinBox">
-             <property name="minimum">
-              <number>1</number>
-             </property>
-             <property name="maximum">
-              <number>999999</number>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <widget class="QLabel" name="label_4">
-             <property name="text">
-              <string>Time stamp from</string>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <widget class="QComboBox" name="cbDTGData">
-             <property name="sizePolicy">
-              <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
-               <horstretch>2</horstretch>
-               <verstretch>0</verstretch>
-              </sizepolicy>
-             </property>
-            </widget>
-           </item>
-           <item>
-            <spacer name="horizontalSpacer">
-             <property name="orientation">
-              <enum>Qt::Horizontal</enum>
-             </property>
-             <property name="sizeHint" stdset="0">
-              <size>
-               <width>40</width>
-               <height>20</height>
-              </size>
-             </property>
-            </spacer>
-           </item>
-          </layout>
-         </item>
         </layout>
        </widget>
       </item>
@@ -200,33 +129,19 @@
       <item>
        <layout class="QGridLayout" name="gridLayout_2">
         <property name="topMargin">
-         <number>20</number>
+         <number>0</number>
         </property>
-        <item row="3" column="0">
-         <widget class="QRadioButton" name="radioButton">
-          <property name="text">
-           <string>Directory</string>
-          </property>
-         </widget>
-        </item>
-        <item row="2" column="0">
+        <item row="2" column="1">
          <widget class="QRadioButton" name="radioButton_2">
           <property name="text">
-           <string>In-Memory</string>
+           <string>in memory</string>
           </property>
           <property name="checked">
            <bool>true</bool>
           </property>
          </widget>
         </item>
-        <item row="0" column="0">
-         <widget class="QLabel" name="label_6">
-          <property name="text">
-           <string>Basename</string>
-          </property>
-         </widget>
-        </item>
-        <item row="2" column="1">
+        <item row="2" column="3">
          <widget class="QFrame" name="frameInMemoryPath">
           <property name="frameShape">
            <enum>QFrame::StyledPanel</enum>
@@ -251,25 +166,40 @@
             <number>0</number>
            </property>
            <item>
-            <widget class="QLabel" name="label_5">
-             <property name="text">
-              <string>/vsimem/</string>
+            <widget class="QgsFileWidget" name="fileWidgetOutputdir">
+             <property name="dialogTitle">
+              <string>Select output directory</string>
              </property>
             </widget>
            </item>
-           <item>
-            <widget class="QLineEdit" name="tbInMemoryPath"/>
-           </item>
           </layout>
          </widget>
         </item>
-        <item row="0" column="1">
-         <widget class="QLineEdit" name="lineEdit"/>
+        <item row="2" column="0">
+         <widget class="QLabel" name="label_5">
+          <property name="text">
+           <string>Save</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2">
+         <widget class="QRadioButton" name="radioButton">
+          <property name="text">
+           <string>in directory</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_6">
+          <property name="text">
+           <string>Prefix</string>
+          </property>
+         </widget>
         </item>
-        <item row="3" column="1">
-         <widget class="QgsFileWidget" name="fileWidgetOutputdir">
-          <property name="dialogTitle">
-           <string>Select output directory</string>
+        <item row="0" column="1" colspan="3">
+         <widget class="QLineEdit" name="tbFilePrefix">
+          <property name="text">
+           <string>timestack</string>
           </property>
          </widget>
         </item>