diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py
index 6fa2c6db713730c63e4ea3d49e8f7702e41be3ef..c306218f9aafde3ce39806643b903b4f0d52fcd0 100644
--- a/timeseriesviewer/main.py
+++ b/timeseriesviewer/main.py
@@ -1,14 +1,13 @@
 # -*- coding: utf-8 -*-
 """
 /***************************************************************************
- EnMAPBox
-                                 A QGIS plugin
- EnMAP-Box V3
+ HUB TimeSeriesViewer
+                                 A QGIS based time series viewer
                               -------------------
         begin                : 2015-08-20
         git sha              : $Format:%H$
-        copyright            : (C) 2015 by HU-Berlin
-        email                : bj@geo.hu-berlin.de
+        copyright            : (C) 2017 by HU-Berlin
+        email                : benjamin.jakimow@geo.hu-berlin.de
  ***************************************************************************/
 
 /***************************************************************************
@@ -23,38 +22,19 @@
 
 # Import the code for the dialog
 import os, sys, re, fnmatch, collections, copy, traceback, six
+import logging
+logger = logging.getLogger(__name__)
 from qgis.core import *
-#os.environ['PATH'] += os.pathsep + r'C:\OSGeo4W64\bin'
 
-from osgeo import gdal, ogr, osr, gdal_array
+from timeseriesviewer.utils import *
 
-DEBUG = True
-import qgis.analysis
-try:
-    from qgis.gui import *
-    import qgis
-    qgis_available = True
-
-    #import console.console_output
-    #console.show_console()
-    #sys.stdout = console.console_output.writeOut()
-    #sys.stderr = console.console_output.writeOut()
 
-except:
-    print('Can not find QGIS instance')
-    qgis_available = False
+DEBUG = True
 
 import numpy as np
-
-import sys, bisect, multiprocessing, site
-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
-from PyQt4.uic.Compiler.qtproxies import QtGui, QtCore
-import code
-import codecs
-
+import multiprocessing
 #abbreviations
-from timeseriesviewer import jp, mkdir, DIR_SITE_PACKAGES, file_search, dprint
+from timeseriesviewer import jp, mkdir, DIR_SITE_PACKAGES, file_search
 from timeseriesviewer.timeseries import *
 
 
@@ -72,93 +52,6 @@ if os.path.exists(path):
 import pyqtgraph as pg
 
 
-class SpatialExtent(QgsRectangle):
-    """
-    Object to keep QgsRectangle and QgsCoordinateReferenceSystem together
-    """
-    @staticmethod
-    def fromMapCanvas(mapCanvas):
-        assert isinstance(mapCanvas, QgsMapCanvas)
-        extent = mapCanvas.extent()
-        crs = mapCanvas.mapSettings().destinationCrs()
-        return SpatialExtent(crs, extent)
-
-    @staticmethod
-    def fromMapLayer(lyr):
-        assert isinstance(lyr, QgsMapLayer)
-        extent = lyr.extent()
-        crs = lyr.crs()
-        return SpatialExtent(crs, extent)
-
-
-    def __init__(self, crs, *args):
-            assert isinstance(crs, QgsCoordinateReferenceSystem)
-            super(SpatialExtent, self).__init__(*args)
-            self.mCrs = crs
-
-    def setCrs(self, crs):
-        assert isinstance(crs, QgsCoordinateReferenceSystem)
-        self.mCrs = crs
-
-    def crs(self):
-        return self.mCrs
-
-    def toCrs(self, crs):
-        assert isinstance(crs, QgsCoordinateReferenceSystem)
-        box = QgsRectangle(self)
-        if self.mCrs != crs:
-            trans = QgsCoordinateTransform(self.mCrs, crs)
-            box = trans.transformBoundingBox(box)
-        return SpatialExtent(crs, box)
-
-    def __copy__(self):
-        return SpatialExtent(self.crs(), QgsRectangle(self))
-
-    def combineExtentWith(self, *args):
-        if args is None:
-            return
-        elif isinstance(args[0], SpatialExtent):
-            extent2 = args[0].toCrs(self.crs())
-            self.combineExtentWith(QgsRectangle(extent2))
-        else:
-            super(SpatialExtent, self).combineExtentWith(*args)
-
-    def setCenter(self, centerPoint, crs=None):
-
-        if crs and crs != self.crs():
-            trans = QgsCoordinateTransform(crs, self.crs())
-            centerPoint = trans.transform(centerPoint)
-
-        delta = centerPoint - self.center()
-        self.setXMaximum(self.xMaximum() + delta.x())
-        self.setXMinimum(self.xMinimum() + delta.x())
-        self.setYMaximum(self.yMaximum() + delta.y())
-        self.setYMinimum(self.yMinimum() + delta.y())
-
-
-    def __cmp__(self, other):
-        if other is None: return 1
-        s = ""
-
-    def __eq__(self, other):
-        s = ""
-
-    def __sub__(self, other):
-        raise NotImplementedError()
-
-    def __mul__(self, other):
-        raise NotImplementedError()
-
-    def upperLeft(self):
-        return self.xMinimum(), self.yMaximum()
-
-    def lowerRight(self):
-        return self.xMaximum(), self.yMinimum()
-
-    def __repr__(self):
-
-        return '{} {} {}'.format(self.upperLeft(), self.lowerRight(), self.crs().authid())
-
 class TsvMimeDataUtils(QObject):
     def __init__(self, mimeData):
         assert isinstance(mimeData, QMimeData)
@@ -1079,7 +972,7 @@ class TimeSeriesViewer:
         D.actionIdentifyMapLayers.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('identifyMapLayers'))
         D.actionAddMapView.triggered.connect(self.spatialTemporalVis.createMapView)
 
-        D.actionAddTSD.triggered.connect(lambda : self.ua_addTSImages())
+        D.actionAddTSD.triggered.connect(lambda : self.addTimeSeriesImages())
         D.actionRemoveTSD.triggered.connect(lambda: self.TS.removeDates(self.ui.dockTimeSeries.selectedTimeSeriesDates()))
         D.actionRefresh.triggered.connect(self.spatialTemporalVis.refresh)
         D.actionLoadTS.triggered.connect(self.loadTimeSeries)
@@ -1304,10 +1197,13 @@ class TimeSeriesViewer:
                 #    w.widget().deleteLater()
         QApplication.processEvents()
 
-    def ua_addTSImages(self, files=None):
+    def addTimeSeriesImages(self, files=None):
         if files is None:
             files = QFileDialog.getOpenFileNames()
 
+            #collect sublayers, if existing
+
+
         if files:
             self.TS.addFiles(files)
 
@@ -1423,8 +1319,8 @@ def run_tests():
             filesImgLS = file_search(dirSrcLS, '20*_BOA.vrt')
             filesImgRE = file_search(dirSrcRE, '*.vrt', recursive=True)
             #filesMsk = file_search(dirSrc, '2014*_Msk.vrt')
-            S.ua_addTSImages(files=filesImgLS[0:2])
-            S.ua_addTSImages(files=filesImgRE[0:2])
+            S.addTimeSeriesImages(files=filesImgLS[0:2])
+            S.addTimeSeriesImages(files=filesImgRE[0:2])
             #S.ua_addTSImages(files=filesImgLS)
             #S.ua_addTSImages(files=filesImgRE)
             #S.ua_loadExampleTS()
@@ -1455,7 +1351,7 @@ def run_tests():
             dirSrcLS = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted'
             filesImgLS = file_search(dirSrcLS, '2014*_BOA.vrt')
             filesMsk = file_search(dirSrcLS, '2014*_Msk.vrt')
-            S.ua_addTSImages(files=filesImgLS)
+            S.addTimeSeriesImages(files=filesImgLS)
             S.ua_addTSMasks(files=filesMsk)
 
         #S.ua_addView(bands=[4,5,3])
diff --git a/timeseriesviewer/sandbox.py b/timeseriesviewer/sandbox.py
index e1d687e6fdf620660356bbc4807c9e8ec2e75a2b..3b65e27ee89c42d1af945df172ad562160dccdba 100644
--- a/timeseriesviewer/sandbox.py
+++ b/timeseriesviewer/sandbox.py
@@ -1,5 +1,7 @@
 from __future__ import absolute_import
 import six, sys, os, gc, re, collections, site, inspect
+import logging
+logger = logging.getLogger(__name__)
 from osgeo import gdal, ogr
 
 from qgis import *
@@ -9,6 +11,7 @@ from PyQt4.QtGui import *
 from PyQt4.QtCore import *
 
 from timeseriesviewer import *
+from timeseriesviewer.utils import *
 
 class SandboxObjects(object):
 
@@ -58,9 +61,24 @@ def sandboxGui():
     if True:
         #load Sentinel-2
         searchDir = r'H:\Sentinel2'
-        files = file_search(searchDir, 'S2*.xml', recursive=True)
-        files = [f+':20m' for f in files]
-        S.loadImageFiles(files[0:5])
+        files = file_search(searchDir, '*MSIL1C.xml', recursive=True)
+
+        subLayerEndings = getSubLayerEndings(files)
+        if len(subLayerEndings) > 0:
+            layerDefinitions = []
+            for i, subLayer in enumerate(subLayerEndings):
+                ldef = QgsSublayersDialog.LayerDefinition()
+                ldef.layerName = subLayer
+                ldef.layerId = i
+                layerDefinitions.append(ldef)
+
+            d = QgsSublayersDialog(QgsSublayersDialog.Gdal, 'Select Sublayers')
+            d.populateLayerTable(layerDefinitions)
+            d.exec_()
+            subLayerEndings = [l.layerName for l in d.selection()]
+
+        files = filterSubLayers(files, subLayerEndings)
+        S.loadImageFiles(files)
 
 
     if False:
diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py
index fd0fca056eeb6ae11003ac5e4b329680909b3b1d..9f6abe08bd0ebf12fa41d84a37a704cbb6f4ec98 100644
--- a/timeseriesviewer/utils.py
+++ b/timeseriesviewer/utils.py
@@ -1,7 +1,180 @@
 
 
 from collections import defaultdict
+from qgis.core import *
+from qgis.gui import *
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from osgeo import gdal
+
 import weakref
+
+def fileSizeString(num, suffix='B', div=1000):
+    """
+    Returns a human-readable file size string.
+    thanks to Fred Cirera
+    http://stackoverflow.com/questions/1094841/reusable-library-to-get-human-readable-version-of-file-size
+    :param num: number in bytes
+    :param suffix: 'B' for bytes by default.
+    :param div: divisor of num, 1000 by default.
+    :return: the file size string
+    """
+    for unit in ['','K','M','G','T','P','E','Z']:
+        if abs(num) < div:
+            return "{:3.1f}{}{}".format(num, unit, suffix)
+        num /= div
+    return "{:.1f} {}{}".format(num, unit, suffix)
+
+class SpatialPoint(QgsPoint):
+    """
+    Object to keep QgsPoint and QgsCoordinateReferenceSystem together
+    """
+
+    @staticmethod
+    def fromMapCanvasCenter(mapCanvas):
+        assert isinstance(mapCanvas, QgsMapCanvas)
+        crs = mapCanvas.mapSettings().destinationCrs()
+        return SpatialPoint(crs, mapCanvas.center())
+
+    def __init__(self, crs, *args):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        super(SpatialPoint, self).__init__(*args)
+        self.mCrs = crs
+
+    def setCrs(self, crs):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        self.mCrs = crs
+
+    def crs(self):
+        return self.mCrs
+
+    def toCrs(self, crs):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        pt = QgsPoint(self)
+        if self.mCrs != crs:
+            trans = QgsCoordinateTransform(self.mCrs, crs)
+            pt = trans.transform(pt)
+        return SpatialPoint(crs, pt)
+
+    def __copy__(self):
+        return SpatialExtent(self.crs(), QgsRectangle(self))
+
+    def __repr__(self):
+        return '{} {} {}'.format(self.x(), self.y(), self.crs().authid())
+
+
+def findParent(qObject, parentType, checkInstance = False):
+    parent = qObject.parent()
+    if checkInstance:
+        while parent != None and not isinstance(parent, parentType):
+            parent = parent.parent()
+    else:
+        while parent != None and type(parent) != parentType:
+            parent = parent.parent()
+    return parent
+
+class SpatialExtent(QgsRectangle):
+    """
+    Object to keep QgsRectangle and QgsCoordinateReferenceSystem together
+    """
+    @staticmethod
+    def fromMapCanvas(mapCanvas, fullExtent=False):
+        assert isinstance(mapCanvas, QgsMapCanvas)
+
+        if fullExtent:
+            extent = mapCanvas.fullExtent()
+        else:
+            extent = mapCanvas.extent()
+        crs = mapCanvas.mapSettings().destinationCrs()
+        return SpatialExtent(crs, extent)
+
+    @staticmethod
+    def fromLayer(mapLayer):
+        assert isinstance(mapLayer, QgsMapLayer)
+        extent = mapLayer.extent()
+        crs = mapLayer.crs()
+        return SpatialExtent(crs, extent)
+
+    def __init__(self, crs, *args):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        super(SpatialExtent, self).__init__(*args)
+        self.mCrs = crs
+
+    def setCrs(self, crs):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        self.mCrs = crs
+
+    def crs(self):
+        return self.mCrs
+
+    def toCrs(self, crs):
+        assert isinstance(crs, QgsCoordinateReferenceSystem)
+        box = QgsRectangle(self)
+        if self.mCrs != crs:
+            trans = QgsCoordinateTransform(self.mCrs, crs)
+            box = trans.transformBoundingBox(box)
+        return SpatialExtent(crs, box)
+
+    def __copy__(self):
+        return SpatialExtent(self.crs(), QgsRectangle(self))
+
+    def combineExtentWith(self, *args):
+        if args is None:
+            return
+        elif isinstance(args[0], SpatialExtent):
+            extent2 = args[0].toCrs(self.crs())
+            self.combineExtentWith(QgsRectangle(extent2))
+        else:
+            super(SpatialExtent, self).combineExtentWith(*args)
+
+        return self
+
+    def setCenter(self, centerPoint, crs=None):
+
+        if crs and crs != self.crs():
+            trans = QgsCoordinateTransform(crs, self.crs())
+            centerPoint = trans.transform(centerPoint)
+
+        delta = centerPoint - self.center()
+        self.setXMaximum(self.xMaximum() + delta.x())
+        self.setXMinimum(self.xMinimum() + delta.x())
+        self.setYMaximum(self.yMaximum() + delta.y())
+        self.setYMinimum(self.yMinimum() + delta.y())
+
+        return self
+
+    def __cmp__(self, other):
+        if other is None: return 1
+        s = ""
+
+    def __eq__(self, other):
+        s = ""
+
+    def __sub__(self, other):
+        raise NotImplementedError()
+
+    def __mul__(self, other):
+        raise NotImplementedError()
+
+    def upperRight(self):
+        return self.xMaximum(), self.yMaximum()
+
+    def upperLeft(self):
+        return self.xMinimum(), self.yMaximum()
+
+    def lowerRight(self):
+        return self.xMaximum(), self.yMinimum()
+
+    def lowerLeft(self):
+        return self.xMinimum(), self.yMinimum()
+
+
+    def __repr__(self):
+
+        return '{} {} {}'.format(self.upperLeft(), self.lowerRight(), self.crs().authid())
+
+
 class KeepRefs(object):
     __refs__ = defaultdict(list)
     def __init__(self):
@@ -12,4 +185,46 @@ class KeepRefs(object):
         for inst_ref in cls.__refs__[cls]:
             inst = inst_ref()
             if inst is not None:
-                yield inst
\ No newline at end of file
+                yield inst
+
+
+
+def filterSubLayers(filePaths, subLayerEndings):
+    """
+    Returns sub layers endings from all gdal Datasets within filePaths
+    :param filePaths:
+    :param subLayerEndings:
+    :return:
+    """
+    results = []
+    if len(subLayerEndings) == 0:
+        return filePaths[:]
+
+    for path in filePaths:
+        try:
+            ds = gdal.Open(path)
+            if ds.RasterCount == 0:
+                for s in ds.GetSubDatasets():
+                    for ending in subLayerEndings:
+                        if s[0].endswith(ending):
+                            results.append(s[0])
+            else:
+                results.append(path)
+        except:
+            pass
+    return results
+
+def getSubLayerEndings(files):
+    subLayerEndings = []
+    for file in files:
+        try:
+            ds = gdal.Open(file)
+            for subLayer in ds.GetSubDatasets():
+                ending = subLayer[0].split(':')[-2:]
+                if ending not in subLayerEndings:
+                    subLayerEndings.append(':'.join(ending))
+        except:
+            s = ""
+            pass
+
+    return subLayerEndings