diff --git a/sandbox/__init__.py b/sandbox/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/sandbox/sandbox.py b/sandbox/sandbox.py
deleted file mode 100644
index 39effc710a4476cd5395c9a467162dae8e6114fb..0000000000000000000000000000000000000000
--- a/sandbox/sandbox.py
+++ /dev/null
@@ -1,197 +0,0 @@
-from __future__ import absolute_import
-import six, sys, os, gc, re, collections, site, inspect, time
-from osgeo import gdal, ogr
-
-from qgis import *
-from qgis.core import *
-from qgis.gui import *
-from PyQt4.QtGui import *
-from PyQt4.QtCore import *
-
-#from timeseriesviewer import DIR_EXAMPLES, jp, dprint
-DIR_SANDBOX = os.path.dirname(__file__)
-
-from itertools import izip_longest
-
-def grouper(iterable, n, fillvalue=None):
-    args = [iter(iterable)] * n
-    return izip_longest(*args, fillvalue=fillvalue)
-
-
-
-class HiddenMapCanvas(QgsMapCanvas):
-
-    sigPixmapCreated = pyqtSignal(QgsRasterLayer, QPixmap)
-
-
-    def __init__(self, *args, **kwds):
-        super(HiddenMapCanvas,self).__init__(*args, **kwds)
-        self.reg = QgsMapLayerRegistry.instance()
-        self.painter = QPainter()
-        self.layerQueue = list()
-        self.mapCanvasRefreshed.connect(self.createPixmap)
-
-    def isBusy(self):
-        return len(self.layerQueue) != 0
-
-    def createPixmap(self, *args):
-        assert len(self.layerQueue) > 0
-
-        pixmap = QPixmap(self.size())
-        self.painter.begin(pixmap)
-        self.map().paint(self.painter)
-        self.painter.end()
-        assert not pixmap.isNull()
-        lyr = self.layerQueue.pop(0)
-
-        assert lyr.extent().intersects(self.extent())
-        self.sigPixmapCreated.emit(lyr, pixmap)
-        self.startSingleLayerRendering()
-
-
-    def startLayerRendering(self, layers):
-        assert isinstance(layers, list)
-        self.layerQueue.extend(layers)
-        self.startSingleLayerRendering()
-
-
-
-    def startSingleLayerRendering(self):
-
-        if len(self.layerQueue) > 0:
-            mapLayer = self.layerQueue[0]
-            self.reg.addMapLayer(mapLayer)
-            lyrSet = [QgsMapCanvasLayer(mapLayer)]
-            self.setLayerSet(lyrSet)
-
-            #todo: add crosshair
-            self.refreshAllLayers()
-
-
-
-n_PX = 0
-def newPixmap(layer, pixmap):
-    global n_PX
-    pathPNG = jp(DIR_SANDBOX, 'mapimage{}.png'.format(n_PX))
-    n_PX += 1
-    # .saveAsImage(pathPNG)
-    # pm = C.layerToPixmap(lyrRef, QSize(600,600))
-    print('Write ' + pathPNG)
-    pixmap.toImage().save(pathPNG)
-
-    s = ""
-
-
-if __name__ == '__main__':
-    import site, sys
-    #add site-packages to sys.path as done by enmapboxplugin.py
-
-    import timeseriesviewer.main
-
-    s = ""
-    from timeseriesviewer import DIR_SITE_PACKAGES
-    site.addsitedir(DIR_SITE_PACKAGES)
-
-    #prepare QGIS environment
-    if sys.platform == 'darwin':
-        PATH_QGS = r'/Applications/QGIS.app/Contents/MacOS'
-        #os.environ['GDAL_DATA'] = r'/usr/local/Cellar/gdal/1.11.3_1/share'
-    else:
-        # assume OSGeo4W startup
-        PATH_QGS = os.environ['QGIS_PREFIX_PATH']
-    assert os.path.exists(PATH_QGS)
-
-    qgsApp = QgsApplication([], True)
-    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns')
-    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns/qgis')
-    qgsApp.setPrefixPath(PATH_QGS, True)
-    qgsApp.initQgis()
-
-    from timeseriesviewer import file_search, PATH_EXAMPLE_TIMESERIES
-    from timeseriesviewer.timeseries import TimeSeries, TimeSeriesDatum
-    pathTestTS = jp(DIR_EXAMPLES, 'ExampleTimeSeries.csv')
-    TS = TimeSeries()
-    if False or not os.path.exists(pathTestTS):
-        paths = file_search(jp(DIR_EXAMPLES, 'Images'), '*.bsq')
-
-        TS.addFiles(paths)
-        TS.saveToFile(PATH_EXAMPLE_TIMESERIES)
-    else:
-        TS.loadFromFile(PATH_EXAMPLE_TIMESERIES)
-
-
-
-
-
-    C = HiddenMapCanvas()
-    C.setAutoFillBackground(True)
-    C.setCanvasColor(Qt.green)
-    #C.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
-
-    #l = QHBoxLayout()
-    #l.addWidget(C)
-    #w.layout().addWidget(C)
-    #w.show()
-
-
-    #C.setFixedSize(QSize(300,400))
-    lyrRef = TS.data[24].lyrImg
-    layers = [TS.data[i].lyrImg for i in [23,12,16]]
-    QgsMapLayerRegistry.instance().addMapLayer(lyrRef, False)
-    C.setDestinationCrs(lyrRef.crs())
-    C.setExtent(lyrRef.extent())
-    C.setFixedSize(QSize(600, 600))
-    C.sigPixmapCreated.connect(newPixmap)
-
-
-    C.startLayerRendering(layers)
-    #w.show()
-    qgsApp.exec_()
-
-    pathPNG = jp(DIR_SANDBOX, 'mapimage.png')
-    #.saveAsImage(pathPNG)
-    #pm = C.layerToPixmap(lyrRef, QSize(600,600))
-    #pm.toImage().save(pathPNG)
-
-
-    #qgsApp.exitQgis()
-    s = ""
-
-    if False:
-        drvMEM = gdal.GetDriverByName('MEM')
-        ds = gdal.Open(TS.data[0].pathImg)
-        ds = drvMEM.CreateCopy('',ds)
-
-        lyr = QgsRasterLayer(paths[0])
-        finalLayerList = []
-        def callback(result):
-            assert isinstance(result, QgsRasterLayer)
-            print(result)
-            finalLayerList.append(result)
-            s =  ""
-        cnt = 0
-        def callbackFin():
-            cnt-=1
-
-        #run
-        #LL = LayerLoaderR(paths)
-        #LL.signales.sigLayerLoaded.connect(callback)
-        #r = LL.run()
-
-        import numpy as np
-        #pool = QThreadPool()
-        pool = QThreadPool.globalInstance()
-        pool.setMaxThreadCount(4)
-        for files in grouper(paths, 3):
-
-            LL = TSDLoader([f for f in files if f is not None])
-            cnt += 1
-            LL.signals.sigRasterLayerLoaded.connect(callback)
-            LL.signals.sigFinished.connect(callbackFin)
-            pool.start(LL)
-
-        t0 = np.datetime64('now')
-        pool.waitForDone()
-
-
-    s = ""
\ No newline at end of file
diff --git a/timeseriesviewer/__init__.py b/timeseriesviewer/__init__.py
index a5e88c502fdc9fc919a2c18b8be77d4c8854f952..29f56bf2fbb87f6c618d385f07a7f6a3387419c6 100644
--- a/timeseriesviewer/__init__.py
+++ b/timeseriesviewer/__init__.py
@@ -1,7 +1,30 @@
 import os, sys, fnmatch, site
-import six
+import six, logging
+logger = logging.getLogger(__name__)
+
 from PyQt4.QtCore import QSettings
 from PyQt4.QtGui import QIcon
+
+DEBUG = True
+
+#initiate loggers for all pyfiles
+import pkgutil
+DIR = os.path.dirname(__file__)
+names = []
+for m, name, ispkg in pkgutil.walk_packages(path=__file__, prefix='timeseriesviewer.'):
+    if name not in names:
+        names.append(name)
+
+for name in names:
+    logger = logging.getLogger(name)
+    logger.setLevel(logging.DEBUG)
+    fh = logging.StreamHandler()
+    fh_formatter = logging.Formatter('%(levelname)s %(lineno)d:%(filename)s%(module)s %(funcName)s \n\t%(message)s')
+    fh.setFormatter(fh_formatter)
+    fh.addFilter(logging.Filter(name))
+    logger.addHandler(fh)
+
+
 jp = os.path.join
 dn = os.path.dirname
 mkdir = lambda p: os.makedirs(p, exist_ok=True)
@@ -35,10 +58,6 @@ def icon():
     return QIcon(':/timeseriesviewer/icons/icon.png')
 
 
-def dprint(text, file=None):
-    if DEBUG:
-        six._print('DEBUG::{}'.format(text), file=file)
-
 
 def file_search(rootdir, wildcard, recursive=False, ignoreCase=False):
     assert rootdir is not None
@@ -58,11 +77,14 @@ def file_search(rootdir, wildcard, recursive=False, ignoreCase=False):
     return results
 
 
-def findAbsolutePath(file):
-    if os.path.exists(file): return file
-    possibleRoots = [DIR_EXAMPLES, DIR_REPO, os.getcwd()]
-    for root in possibleRoots:
-        tmp = jp(root, file)
-        if os.path.exists(tmp):
-            return tmp
-    return None
\ No newline at end of file
+
+def getFileAndAttributes(file):
+    """
+    splits a GDAL valid file path into
+    :param file:
+    :return:
+    """
+    dn = os.path.dirname(file)
+    bn = os.path.basename(file)
+    bnSplit = bn.split(':')
+    return os.path.join(dn,bnSplit[0]), ':'.join(bnSplit[1:])
\ No newline at end of file
diff --git a/timeseriesviewer/crosshair.py b/timeseriesviewer/crosshair.py
new file mode 100644
index 0000000000000000000000000000000000000000..c13e11c034bdcca22626224931b489fc2c964653
--- /dev/null
+++ b/timeseriesviewer/crosshair.py
@@ -0,0 +1,433 @@
+
+import os
+
+from qgis.core import *
+from qgis.gui import *
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+import numpy as np
+from timeseriesviewer import *
+from timeseriesviewer.utils import *
+
+from timeseriesviewer.ui.widgets import loadUIFormClass
+
+load = lambda p : loadUIFormClass(jp(DIR_UI,p))
+
+class CrosshairStyle(object):
+    def __init__(self, **kwds):
+
+        self.mColor = QColor.fromRgb(255,0,0, 125)
+        self.mThickness = 1 #in px
+        self.mSize = 1.0 #normalized
+        self.mGap = 0.05 #normalized
+        self.mShowDot = True
+        self.mDotSize = 1 #in px
+        self.mSizePixelBorder = 1
+        self.mShow = True
+        self.mShowPixelBorder = True
+
+
+    def setColor(self, color):
+        assert isinstance(color, QColor)
+        self.mColor = color
+
+    def setSize(self, size):
+        self.mSize = self._normalize(size)
+
+    def setDotSize(self, size):
+        assert size >= 0
+        self.mDotSize = size
+
+    def setThickness(self, size):
+        """
+        Crosshair thickness in px
+        :param size:
+        :return:
+        """
+        assert size >= 0
+        self.mThickness = size
+
+    def setShowPixelBorder(self, b):
+        assert isinstance(b, bool)
+        self.mShowPixelBorder = b
+
+    def setGap(self, gapSize):
+        """
+        Set gap size in % [0, 100] or normalized coordinates [0,1]
+        :param gapSize:
+        :return:
+        """
+        self.mGap = self._normalize(gapSize)
+
+    def _normalize(self, size):
+        assert size >= 0 and size <= 100
+        size = float(size)
+        if size > 1:
+            size /= 100
+        return size
+
+    def setShowDot(self, b):
+        assert isinstance(b, bool)
+        self.mShowDot = b
+
+    def setShow(self, b):
+        assert isinstance(b, bool)
+        self.mShow = b
+
+    def rendererV2(self):
+        """
+        Returns the vector layer renderer
+        :return:
+        """
+        registry = QgsSymbolLayerV2Registry.instance()
+        lineMeta = registry.symbolLayerMetadata("SimpleLine")
+        lineLayer = lineMeta.createSymbolLayer({})
+        lineLayer.setColor(self.mColor)
+        lineLayer.setPenStyle(Qt.SolidLine)
+
+        lineLayer.setWidth(self.mThickness)
+        lineLayer.setWidthUnit(2) #pixel
+        #lineLayer.setWidth(self.mThickness)
+
+        """
+        lineLayer = lineMeta.createSymbolLayer(
+            {'width': '0.26',
+             'color': self.mColor,
+             'offset': '0',
+             'penstyle': 'solid',
+             'use_custom_dash': '0'})
+        """
+
+        # Replace the default layer with our custom layer
+
+        symbol = QgsLineSymbolV2([])
+        symbol.deleteSymbolLayer(0)
+        symbol.appendSymbolLayer(lineLayer)
+        return QgsSingleSymbolRendererV2(symbol)
+
+class CrosshairMapCanvasItem(QgsMapCanvasItem):
+
+    def __init__(self, mapCanvas):
+        assert isinstance(mapCanvas, QgsMapCanvas)
+        super(CrosshairMapCanvasItem, self).__init__(mapCanvas)
+
+        self.canvas = mapCanvas
+        self.rasterGridLayer = None
+        self.sizePixelBox = 0
+        self.sizePixelBox = 1
+        self.mShow = True
+        self.crosshairStyle = CrosshairStyle()
+        self.crosshairStyle.setShow(False)
+        self.setCrosshairStyle(self.crosshairStyle)
+
+    def setShow(self, b):
+        assert isinstance(b, bool)
+        old = self.mShow
+        self.mShow = b
+        self.crosshairStyle.setShow(b)
+        if old != b:
+            self.canvas.update()
+
+
+    def connectRasterGrid(self, qgsRasterLayer):
+
+        if isinstance(qgsRasterLayer):
+            self.rasterGridLayer = qgsRasterLayer
+        else:
+            self.rasterGridLayer = None
+
+    def setPixelBox(self, nPx):
+        assert nPx >= 0
+        assert nPx == 1 or nPx % 3 == 0, 'Size of pixel box must be an odd integer'
+        self.sizePixelBox = nPx
+
+
+    def setCrosshairStyle(self, crosshairStyle):
+        assert isinstance(crosshairStyle, CrosshairStyle)
+        self.crosshairStyle = crosshairStyle
+
+        #apply style
+        self.canvas.update()
+        #self.updateCanvas()
+
+    def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
+        if self.mShow and self.crosshairStyle.mShow:
+           #paint the crosshair
+            size = self.canvas.size()
+            centerGeo = self.canvas.center()
+            centerPx = self.toCanvasCoordinates(self.canvas.center())
+
+            x0 = centerPx.x() * (1.0 - self.crosshairStyle.mSize)
+            y0 = centerPx.y() * (1.0 - self.crosshairStyle.mSize)
+            x1 = size.width() - x0
+            y1 = size.height() - y0
+            gap = min([centerPx.x(), centerPx.y()]) * self.crosshairStyle.mGap
+
+            #this is what we want to draw
+            lines = []
+            polygons = []
+
+            lines.append(QLineF(x0, centerPx.y(), centerPx.x() - gap, centerPx.y()))
+            lines.append(QLineF(x1, centerPx.y(), centerPx.x() + gap, centerPx.y()))
+            lines.append(QLineF(centerPx.x(), y0, centerPx.x(), centerPx.y() - gap))
+            lines.append(QLineF(centerPx.x(), y1, centerPx.x(), centerPx.y() + gap))
+
+            if self.crosshairStyle.mShowDot:
+                p = QRectF()
+
+                d = int(round(max([1,self.crosshairStyle.mDotSize * 0.5])))
+
+                p.setTopLeft(QPointF(centerPx.x() - d,
+                                     centerPx.y() + d))
+                p.setBottomRight(QPointF(centerPx.x() + d,
+                                         centerPx.y() - d))
+
+                p = QPolygonF(p)
+                polygons.append(p)
+
+            if self.crosshairStyle.mShowPixelBorder:
+                rasterLayers = [l for l in self.canvas.layers() if isinstance(l, QgsRasterLayer)
+                                and l.isValid()]
+
+                if len(rasterLayers) > 0:
+
+                    lyr = rasterLayers[0]
+
+                    ns = lyr.width()  # ns = number of samples = number of image columns
+                    nl = lyr.height()  # nl = number of lines
+                    ex = lyr.extent()
+                    xres = lyr.rasterUnitsPerPixelX()
+                    yres = lyr.rasterUnitsPerPixelY()
+
+                    ms = self.canvas.mapSettings()
+                    centerPxLyr = ms.mapToLayerCoordinates(lyr, centerGeo)
+
+
+                    m2p = self.canvas.mapSettings().mapToPixel()
+                    #get center pixel pixel index
+                    pxX = int(np.floor((centerPxLyr.x() - ex.xMinimum()) / xres).astype(int))
+                    pxY = int(np.floor((ex.yMaximum() - centerPxLyr.y()) / yres).astype(int))
+
+
+                    def px2LayerGeo(x, y):
+                        x2 = ex.xMinimum() + (x * xres)
+                        y2 = ex.yMaximum() - (y * yres)
+                        return QgsPoint(x2,y2)
+                    lyrCoord2CanvasPx = lambda x, y, : self.toCanvasCoordinates(
+                        ms.layerToMapCoordinates(lyr,
+                                                 px2LayerGeo(x, y)))
+                    if pxX >= 0 and pxY >= 0 and \
+                       pxX < ns and pxY < nl:
+                        #get pixel edges in map canvas coordinates
+
+                        lyrGeo = px2LayerGeo(pxX, pxY)
+                        mapGeo = ms.layerToMapCoordinates(lyr, lyrGeo)
+                        canCor = self.toCanvasCoordinates(mapGeo)
+
+                        ul = lyrCoord2CanvasPx(pxX, pxY)
+                        ur = lyrCoord2CanvasPx(pxX+1, pxY)
+                        lr = lyrCoord2CanvasPx(pxX+1, pxY+1)
+                        ll = lyrCoord2CanvasPx(pxX, pxY+1)
+
+                        pixelBorder = QPolygonF()
+                        pixelBorder.append(ul)
+                        pixelBorder.append(ur)
+                        pixelBorder.append(lr)
+                        pixelBorder.append(ll)
+                        pixelBorder.append(ul)
+
+                        pen = QPen(Qt.SolidLine)
+                        pen.setWidth(self.crosshairStyle.mSizePixelBorder)
+                        pen.setColor(self.crosshairStyle.mColor)
+                        pen.setBrush(self.crosshairStyle.mColor)
+                        brush = QBrush(Qt.NoBrush)
+                        brush.setColor(self.crosshairStyle.mColor)
+                        painter.setBrush(brush)
+                        painter.setPen(pen)
+                        painter.drawPolygon(pixelBorder)
+
+            pen = QPen(Qt.SolidLine)
+            pen.setWidth(self.crosshairStyle.mThickness)
+            pen.setColor(self.crosshairStyle.mColor)
+            pen.setBrush(self.crosshairStyle.mColor)
+            brush = QBrush(Qt.NoBrush)
+            brush.setColor(self.crosshairStyle.mColor)
+            painter.setBrush(brush)
+            painter.setPen(pen)
+            for p in polygons:
+                painter.drawPolygon(p)
+            for p in lines:
+                painter.drawLine(p)
+
+
+
+
+
+class CrosshairWidget(QWidget, load('crosshairwidget.ui')):
+    sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
+
+    def __init__(self, title='<#>', parent=None):
+        super(CrosshairWidget, self).__init__(parent)
+        self.setupUi(self)
+
+        #self.crossHairReferenceLayer = CrosshairLayer()
+        #self.crossHairReferenceLayer.connectCanvas(self.crossHairCanvas)
+
+        self.mapCanvas.setExtent(QgsRectangle(0, 0, 1, 1))  #
+        #QgsMapLayerRegistry.instance().addMapLayer(self.crossHairReferenceLayer)
+        #self.crossHairCanvas.setLayerSet([QgsMapCanvasLayer(self.crossHairReferenceLayer)])
+
+        #crs = QgsCoordinateReferenceSystem('EPSG:25832')
+        #self.crossHairCanvas.mapSettings().setDestinationCrs(crs)
+
+        self.mapCanvasItem = CrosshairMapCanvasItem(self.mapCanvas)
+
+        self.btnCrosshairColor.colorChanged.connect(self.refreshCrosshairPreview)
+        self.spinBoxCrosshairAlpha.valueChanged.connect(self.refreshCrosshairPreview)
+        self.spinBoxCrosshairThickness.valueChanged.connect(self.refreshCrosshairPreview)
+        self.spinBoxCrosshairSize.valueChanged.connect(self.refreshCrosshairPreview)
+        self.spinBoxCrosshairGap.valueChanged.connect(self.refreshCrosshairPreview)
+        self.spinBoxDotSize.valueChanged.connect(self.refreshCrosshairPreview)
+        self.cbCrosshairShowDot.toggled.connect(self.refreshCrosshairPreview)
+        self.cbShowPixelBoundaries.toggled.connect(self.refreshCrosshairPreview)
+        self.refreshCrosshairPreview()
+
+
+
+    def setCanvasColor(self, color):
+        self.mapCanvas.setBackgroundColor(color)
+        self.btnMapCanvasColor.colorChanged.connect(self.onMapCanvasColorChanged)
+
+    def onMapCanvasColorChanged(self, color):
+        self.sigMapCanvasColorChanged.emit(color)
+        self.refreshCrosshairPreview()
+
+    def mapCanvasColor(self):
+        return self.btnMapCanvasColor.color()
+
+    def refreshCrosshairPreview(self, *args):
+        style = self.crosshairStyle()
+        self.mapCanvasItem.setCrosshairStyle(style)
+        #self.crossHairReferenceLayer.setCrosshairStyle(style)
+        #self.crossHairCanvas.refreshAllLayers()
+        self.sigCrosshairStyleChanged.emit(style)
+
+    def setCrosshairStyle(self, style):
+        assert isinstance(style, CrosshairStyle)
+        self.btnCrosshairColor.setColor(style.mColor)
+        self.spinBoxCrosshairAlpha.setValue(style.mColor.alpha())
+        self.spinBoxCrosshairThickness.setValue(style.mThickness)
+        self.spinBoxCrosshairSize.setValue(int(style.mSize*100))
+        self.spinBoxCrosshairGap.setValue(int(style.mGap*100))
+        self.spinBoxDotSize.setValue(style.mDotSize)
+        self.cbCrosshairShowDot.setChecked(style.mShowDot)
+        self.cbShowPixelBoundaries.setChecked(style.mShowPixelBorder)
+
+    def crosshairStyle(self):
+        style = CrosshairStyle()
+        c = self.btnCrosshairColor.color()
+        c.setAlpha(self.spinBoxCrosshairAlpha.value())
+        style.setColor(c)
+        style.setThickness(self.spinBoxCrosshairThickness.value())
+        style.setSize(self.spinBoxCrosshairSize.value())
+        style.setGap(self.spinBoxCrosshairGap.value())
+        style.setDotSize(self.spinBoxDotSize.value())
+        style.setShowDot(self.cbCrosshairShowDot.isChecked())
+        style.setShowPixelBorder(self.cbShowPixelBoundaries.isChecked())
+        return style
+
+class CrosshairDialog(QgsDialog):
+
+    @staticmethod
+    def getCrosshairStyle(*args, **kwds):
+        """
+        Opens a CrosshairDialog.
+        :param args:
+        :param kwds:
+        :return: specified CrosshairStyle if accepted, else None
+        """
+        d = CrosshairDialog(*args, **kwds)
+        d.exec_()
+
+        if d.result() == QDialog.Accepted:
+            return d.crosshairStyle()
+        else:
+
+            return None
+
+    def __init__(self, parent=None, crosshairStyle=None, mapCanvas=None, title='Specify Crosshair'):
+        super(CrosshairDialog, self).__init__(parent=parent , \
+            buttons=QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+        self.w = CrosshairWidget(parent=self)
+        self.setWindowTitle(title)
+        self.btOk = QPushButton('Ok')
+        self.btCancel = QPushButton('Cance')
+        buttonBar = QHBoxLayout()
+        #buttonBar.addWidget(self.btCancel)
+        #buttonBar.addWidget(self.btOk)
+        l = self.layout()
+        l.addWidget(self.w)
+        l.addLayout(buttonBar)
+        #self.setLayout(l)
+
+        if isinstance(mapCanvas, QgsMapCanvas):
+            self.setMapCanvas(mapCanvas)
+
+        if isinstance(crosshairStyle, CrosshairStyle):
+            self.setCrosshairStyle(crosshairStyle)
+        s = ""
+
+    def crosshairStyle(self):
+        return self.w.crosshairStyle()
+
+    def setCrosshairStyle(self, crosshairStyle):
+        assert isinstance(crosshairStyle, CrosshairStyle)
+        self.w.setCrosshairStyle(crosshairStyle)
+
+    def setMapCanvas(self, mapCanvas):
+        assert isinstance(mapCanvas, QgsMapCanvas)
+        # copy layers
+        canvas = self.w.mapCanvasItem.canvas
+        lyrs = []
+        for lyr in mapCanvas.layers():
+            s = ""
+        lyrs = mapCanvas.layers()
+        canvas.setLayerSet([QgsMapCanvasLayer(l) for l in lyrs])
+        canvas.mapSettings().setDestinationCrs(mapCanvas.mapSettings().destinationCrs())
+        canvas.setExtent(mapCanvas.extent())
+        canvas.setCenter(mapCanvas.center())
+        canvas.setCanvasColor(mapCanvas.canvasColor())
+        canvas.refresh()
+        canvas.updateMap()
+        canvas.refreshAllLayers()
+
+if __name__ == '__main__':
+    import site, sys
+    #add site-packages to sys.path as done by enmapboxplugin.py
+
+    from timeseriesviewer import sandbox
+    qgsApp = sandbox.initQgisEnvironment()
+
+    if False:
+        c = QgsMapCanvas()
+        c.setExtent(QgsRectangle(0,0,1,1))
+        i = CrosshairMapCanvasItem(c)
+        i.setShow(True)
+        s = CrosshairStyle()
+        s.setShow(True)
+        i.setCrosshairStyle(s)
+        c.show()
+
+
+    import example.Images
+    lyr = QgsRasterLayer(example.Images.Img_2012_05_09_LE72270652012130EDC00_BOA)
+    QgsMapLayerRegistry.instance().addMapLayer(lyr)
+    refCanvas = QgsMapCanvas()
+    refCanvas.setLayerSet([QgsMapCanvasLayer(lyr)])
+    refCanvas.setExtent(lyr.extent())
+    refCanvas.show()
+
+    style = CrosshairDialog.getCrosshairStyle(mapCanvas=refCanvas)
+
+    qgsApp.exec_()
+    qgsApp.exitQgis()
diff --git a/timeseriesviewer/dateparser.py b/timeseriesviewer/dateparser.py
index c6cda9fb29294b781697da59a1a86ab80306a0fb..2a73f0ee2b6f804e745b7ee53322f30787f039cb 100644
--- a/timeseriesviewer/dateparser.py
+++ b/timeseriesviewer/dateparser.py
@@ -67,18 +67,21 @@ class ImageDateParser(object):
 class ImageDateParserGeneric(ImageDateParser):
     def __init__(self, dataSet):
         super(ImageDateParserGeneric, self).__init__(dataSet)
-        self.regDateKeys = re.compile('(acquisition[ ]*time|date)')
+        self.regDateKeys = re.compile('(acquisition[ ]*time|datetime)', re.IGNORECASE)
 
     def parseDate(self):
         # search metadata for datetime information
         # see http://www.harrisgeospatial.com/docs/ENVIHeaderFiles.html for datetime format
+        dtg = None
         for domain in self.dataSet.GetMetadataDomainList():
             md = self.dataSet.GetMetadata_Dict(domain)
             for key, value in md.items():
                 if self.regDateKeys.search(key):
-                    dtg = extractDateTimeGroup(regISODate, value)
-                    if dtg:
+                    try:
+                        dtg = np.datetime64(value)
                         return dtg
+                    except:
+                        pass
 
         # search for ISO date in basename
         # search in basename
@@ -90,22 +93,56 @@ class ImageDateParserGeneric(ImageDateParser):
         return dtg
 
 
+class ImageDateParserPLEIADES(ImageDateParser):
+    def __init__(self, dataSet):
+        super(ImageDateParserPLEIADES, self).__init__(dataSet)
+
+    def parseDate(self):
+        timeStamp = ''
+        ext = self.extension.lower()
+
+        if ext == '.xml':
+            md = self.dataSet.GetMetadata_Dict()
+            if 'IMAGING_DATE' in md.keys() and 'IMAGING_TIME' in md.keys():
+                timeStamp = '{}T{}'.format(md.get('IMAGING_DATE', ''),
+                                           md.get('IMAGING_TIME', ''))
+        elif ext == '.jp2':
+            timeStamp = self.dataSet.GetMetadataItem('ACQUISITIONDATETIME', 'IMAGERY')
+        if len(timeStamp) > 0:
+            return np.datetime64(timeStamp)
+        return None
+
+
+class ImageDateParserSentinel2(ImageDateParser):
+    def __init__(self, dataSet):
+        super(ImageDateParserSentinel2, self).__init__(dataSet)
+
+    def parseDate(self):
+        timeStamp = ''
+        ext = self.extension.lower()
+
+        if ext == '.xml':
+            md = self.dataSet.GetMetadata_Dict()
+            timeStamp = md.get('DATATAKE_1_DATATAKE_SENSING_START', '')
+        if len(timeStamp) > 0:
+            return np.datetime64(timeStamp)
+        return None
 
-class ImageDataParserLandsat(ImageDateParser):
+class ImageDateParserLandsat(ImageDateParser):
     #see https://landsat.usgs.gov/what-are-naming-conventions-landsat-scene-identifiers
     regLandsatSceneID  = re.compile(r'L[COTEM][4578]\d{3}\d{3}\d{4}\d{3}[A-Z]{2}[A-Z1]\d{2}')
     regLandsatProductID = re.compile(r'L[COTEM]0[78]_(L1TP|L1GT|L1GS)_\d{3}\d{3}_\d{4}\d{2}\d{2}_\d{4}\d{2}\d{2}_0\d{1}_(RT|T1|T2)')
 
     def __init__(self, dataSet):
-        super(ImageDataParserLandsat, self).__init__(dataSet)
+        super(ImageDateParserLandsat, self).__init__(dataSet)
 
     def parseDate(self):
         #search for LandsatSceneID (old) and Landsat Product IDs (new)
-        sceneID = matchOrNone(ImageDataParserLandsat.regLandsatSceneID, self.baseName)
+        sceneID = matchOrNone(ImageDateParserLandsat.regLandsatSceneID, self.baseName)
         if sceneID:
             return getDateTime64FromYYYYDOY(sceneID[9:16])
 
-        productID = matchOrNone(ImageDataParserLandsat.regLandsatProductID, self.baseName)
+        productID = matchOrNone(ImageDateParserLandsat.regLandsatProductID, self.baseName)
         if productID:
             return np.datetim64(productID[17:25])
         return None
@@ -118,7 +155,6 @@ dateParserList.insert(0, dateParserList.pop(dateParserList.index(ImageDateParser
 def parseDateFromDataSet(dataSet):
     assert isinstance(dataSet, gdal.Dataset)
     for parser in dateParserList:
-        print(parser)
         dtg = parser(dataSet).parseDate()
         if dtg:
             return dtg
diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py
index 6fa2c6db713730c63e4ea3d49e8f7702e41be3ef..1849ed2ab79aa6e748e39f2c58190dbe6aedc427 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)
@@ -359,7 +252,9 @@ class MapView(QObject):
 
     sigTitleChanged = pyqtSignal(str)
     sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer)
-
+    from timeseriesviewer.crosshair import CrosshairStyle
+    sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
+    sigShowCrosshair = pyqtSignal(bool)
     sigVectorLayerChanged = pyqtSignal()
 
     sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
@@ -382,6 +277,7 @@ class MapView(QObject):
         self.spatialExtent = None
         self.ui.actionRemoveMapView.triggered.connect(lambda: self.sigRemoveMapView.emit(self))
         self.ui.actionApplyStyles.triggered.connect(self.applyStyles)
+        self.ui.actionShowCrosshair.toggled.connect(self.setShowCrosshair)
         self.ui.sigShowMapView.connect(lambda: self.sigMapViewVisibility.emit(True))
         self.ui.sigHideMapView.connect(lambda: self.sigMapViewVisibility.emit(False))
         self.ui.sigVectorVisibility.connect(self.sigVectorVisibility.emit)
@@ -433,6 +329,10 @@ class MapView(QObject):
     def title(self):
         return self.mTitle
 
+    def setCrosshairStyle(self, crosshairStyle):
+        self.sigCrosshairStyleChanged.emit(crosshairStyle)
+    def setShowCrosshair(self, b):
+        self.sigShowCrosshair.emit(b)
 
     def removeSensor(self, sensor):
         assert type(sensor) is SensorInstrument
@@ -564,7 +464,7 @@ class TimeSeriesDatumView(QObject):
 
         i = self.MVC.index(mapView)
 
-        from timeseriesviewer.ui.widgets import TsvMapCanvas
+        from timeseriesviewer.mapcanvas import TsvMapCanvas
         canvas = TsvMapCanvas(self, mapView, parent=self.ui)
 
         canvas.setFixedSize(self.subsetSize)
@@ -627,6 +527,12 @@ class SpatialTemporalVisualization(QObject):
         self.setSpatialExtent(self.TS.getMaxSpatialExtent())
         self.setSubsetSize(QSize(100,50))
 
+    def setCrosshairStyle(self, crosshairStyle):
+        self.MVC.setCrosshairStyle(crosshairStyle)
+
+    def setShowCrosshair(self, b):
+        self.MVC.setShowCrosshair(b)
+
     def setVectorLayer(self, lyr):
         self.MVC.setVectorLayer(lyr)
 
@@ -882,10 +788,19 @@ class MapViewCollection(QObject):
         self.mapViewsDefinitions = []
         self.mapViewButtons = dict()
         self.adjustScrollArea()
+
     def applyStyles(self):
         for mapView in self.mapViewsDefinitions:
             mapView.applyStyles()
 
+    def setCrosshairStyle(self, crosshairStyle):
+        for mapView in self.mapViewsDefinitions:
+            mapView.setCrosshairStyle(crosshairStyle)
+
+    def setShowCrosshair(self, b):
+        for mapView in self.mapViewsDefinitions:
+            mapView.setShowCrosshair(b)
+
     def index(self, mapView):
         assert isinstance(mapView, MapView)
         return self.mapViewsDefinitions.index(mapView)
@@ -1079,7 +994,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)
@@ -1087,6 +1002,7 @@ class TimeSeriesViewer:
         D.actionSaveTS.triggered.connect(self.ua_saveTSFile)
         D.actionAddTSExample.triggered.connect(self.ua_loadExampleTS)
 
+        D.actionShowCrosshair.toggled.connect(self.spatialTemporalVis.setShowCrosshair)
 
         #connect buttons with actions
         from timeseriesviewer.ui.widgets import AboutDialogUI, PropertyDialogUI
@@ -1304,10 +1220,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 +1342,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 +1374,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/mapcanvas.py b/timeseriesviewer/mapcanvas.py
new file mode 100644
index 0000000000000000000000000000000000000000..66a36cacc527d1e1d1d0667ed4be9d1d455e4852
--- /dev/null
+++ b/timeseriesviewer/mapcanvas.py
@@ -0,0 +1,432 @@
+
+import os, logging
+
+logger = logging.getLogger(__name__)
+
+from qgis.core import *
+from qgis.gui import *
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+from timeseriesviewer import SETTINGS
+from timeseriesviewer.utils import *
+from timeseriesviewer.ui.widgets import TsvScrollArea
+
+class TsvMapCanvas(QgsMapCanvas):
+    from timeseriesviewer.main import SpatialExtent
+    saveFileDirectories = dict()
+    sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
+    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
+
+    def __init__(self, tsdView, mapView, parent=None):
+        super(TsvMapCanvas, self).__init__(parent=parent)
+        from timeseriesviewer.main import TimeSeriesDatumView, MapView
+        assert isinstance(tsdView, TimeSeriesDatumView)
+        assert isinstance(mapView, MapView)
+
+        #the canvas
+        self.setCrsTransformEnabled(True)
+        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
+        self.setCanvasColor(SETTINGS.value('CANVAS_BACKGROUND_COLOR', QColor(0, 0, 0)))
+        self.setContextMenuPolicy(Qt.DefaultContextMenu)
+
+        self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
+
+        self.scrollArea = tsdView.scrollArea
+        assert isinstance(self.scrollArea, TsvScrollArea)
+        self.scrollArea.sigResized.connect(self.setRenderMe)
+        self.scrollArea.horizontalScrollBar().valueChanged.connect(self.setRenderMe)
+
+        self.tsdView = tsdView
+        self.mapView = mapView
+
+        from timeseriesviewer.crosshair import CrosshairMapCanvasItem
+        self.crosshairItem = CrosshairMapCanvasItem(self)
+
+
+        self.vectorLayer = None
+
+        self.mapView.sigVectorLayerChanged.connect(self.refresh)
+        self.mapView.sigVectorVisibility.connect(self.refresh)
+
+        self.renderMe = False
+        self.setRenderMe()
+
+        self.sensorView = self.mapView.sensorViews[self.tsdView.Sensor]
+        self.mapView.sigMapViewVisibility.connect(self.refresh)
+        self.mapView.sigSpatialExtentChanged.connect(self.setSpatialExtent)
+        self.mapView.sigCrosshairStyleChanged.connect(self.setCrosshairStyle)
+        self.mapView.sigShowCrosshair.connect(self.setShowCrosshair)
+        self.referenceLayer = QgsRasterLayer(self.tsdView.TSD.pathImg)
+
+
+
+
+
+        self.sensorView.sigSensorRendererChanged.connect(self.setRenderer)
+        self.setRenderer(self.sensorView.layerRenderer())
+
+
+        self.MAPTOOLS = dict()
+        self.MAPTOOLS['zoomOut'] = QgsMapToolZoom(self, True)
+        self.MAPTOOLS['zoomIn'] = QgsMapToolZoom(self, False)
+        self.MAPTOOLS['pan'] = QgsMapToolPan(self)
+        from timeseriesviewer.maptools import PointMapTool, PointLayersMapTool
+        mt = PointMapTool(self)
+        mt.sigCoordinateSelected.connect(self.sigShowProfiles.emit)
+        self.MAPTOOLS['identifyProfile'] = mt
+
+        self.refresh()
+
+
+    def mapLayersToRender(self, *args):
+        """Returns the map layers actually to be rendered"""
+        mapLayers = []
+
+        if self.mapView.visibleVectorOverlay():
+            #if necessary, register new vector layer
+            refLyr = self.mapView.vectorLayer
+            refUri = refLyr.dataProvider().dataSourceUri()
+
+            if self.vectorLayer is None or self.vectorLayer.dataProvider().dataSourceUri() != refUri:
+                providerKey = refLyr.dataProvider().name()
+                baseName = os.path.basename(refUri)
+                self.vectorLayer = QgsVectorLayer(refUri, baseName, providerKey)
+
+            #update layer style
+            self.vectorLayer.setRendererV2(refLyr.rendererV2().clone())
+            mapLayers.append(self.vectorLayer)
+
+        if self.referenceLayer:
+            mapLayers.append(self.referenceLayer)
+
+        return mapLayers
+
+
+
+    def setLayerSet(self, *args):
+        raise DeprecationWarning()
+
+    def setLayers(self, mapLayers):
+        reg = QgsMapLayerRegistry.instance()
+        for l in mapLayers:
+            reg.addMapLayer(l)
+        super(TsvMapCanvas,self).setLayerSet([QgsMapCanvasLayer(l) for l in mapLayers])
+
+    def refresh(self):
+        self.setLayers(self.mapLayersToRender())
+        self.setRenderMe()
+        super(TsvMapCanvas, self).refresh()
+
+
+    def setCrosshairStyle(self,crosshairStyle):
+        from timeseriesviewer.crosshair import CrosshairStyle
+        if crosshairStyle is None:
+            self.crosshairItem.crosshairStyle.setShow(False)
+            self.crosshairItem.update()
+        else:
+            assert isinstance(crosshairStyle, CrosshairStyle)
+            self.crosshairItem.setCrosshairStyle(crosshairStyle)
+
+    def setShowCrosshair(self,b):
+        self.crosshairItem.setShow(b)
+
+    def setRenderMe(self):
+        oldFlag = self.renderFlag()
+
+        newFlag = self.visibleRegion().boundingRect().isValid() and self.isVisible() and self.tsdView.TSD.isVisible()
+        if oldFlag != newFlag:
+            self.setRenderFlag(newFlag)
+        #print((self.tsdView.TSD, self.renderFlag()))
+        #return b.isValid()
+
+    def pixmap(self):
+        """
+        Returns the current map image as pixmap
+        :return:
+        """
+        return QPixmap(self.map().contentImage().copy())
+
+
+
+    def contextMenuEvent(self, event):
+        menu = QMenu()
+        # add general options
+        menu.addSeparator()
+        action = menu.addAction('Stretch using current Extent')
+        action.triggered.connect(self.stretchToCurrentExtent)
+        action = menu.addAction('Zoom to Layer')
+        action.triggered.connect(lambda : self.setExtent(SpatialExtent(self.referenceLayer.crs(),self.referenceLayer.extent())))
+        menu.addSeparator()
+
+        action = menu.addAction('Change crosshair style')
+        from timeseriesviewer.crosshair import CrosshairDialog
+        action.triggered.connect(lambda : self.setCrosshairStyle(
+                CrosshairDialog.getCrosshairStyle(parent=self,
+                                                mapCanvas=self,
+                                                crosshairStyle=self.crosshairItem.crosshairStyle)
+                ))
+
+        if self.crosshairItem.crosshairStyle.mShow:
+            action = menu.addAction('Hide crosshair')
+            action.triggered.connect(lambda : self.setShowCrosshair(False))
+        else:
+            action = menu.addAction('Show crosshair')
+            action.triggered.connect(lambda: self.setShowCrosshair(True))
+
+        menu.addSeparator()
+
+        m = menu.addMenu('Copy...')
+        action = m.addAction('Map to Clipboard')
+        action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
+        action = m.addAction('Image Path')
+        action.triggered.connect(lambda: QApplication.clipboard().setText(self.tsdView.TSD.pathImg))
+        action = m.addAction('Image Style')
+        #action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.tsdView.TSD.pathImg))
+
+        m = menu.addMenu('Save as...')
+        action = m.addAction('PNG')
+        action.triggered.connect(lambda : self.saveMapImageDialog('PNG'))
+        action = m.addAction('JPEG')
+        action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))
+
+        from timeseriesviewer.main import QgisTsvBridge
+        bridge = QgisTsvBridge.instance()
+        if bridge:
+            assert isinstance(bridge, QgisTsvBridge)
+            action = m.addAction('Add layer to QGIS')
+            action = m.addAction('Import extent from QGIS')
+            action = m.addAction('Export extent to QGIS')
+            s = ""
+
+
+
+
+        menu.addSeparator()
+        TSD = self.tsdView.TSD
+        action = menu.addAction('Hide date')
+        action.triggered.connect(lambda : self.tsdView.TSD.setVisibility(False))
+        action = menu.addAction('Remove date')
+        action.triggered.connect(lambda: TSD.timeSeries.removeDates([TSD]))
+        action = menu.addAction('Remove map view')
+        action.triggered.connect(lambda: self.mapView.sigRemoveMapView.emit(self.mapView))
+        action = menu.addAction('Hide map view')
+        action.triggered.connect(lambda: self.mapView.sigHideMapView.emit())
+
+
+        menu.exec_(event.globalPos())
+
+    def stretchToCurrentExtent(self):
+        results = dict()
+        se = self.spatialExtent()
+
+        for l in self.layers():
+            if isinstance(l, QgsRasterLayer):
+                r = l.renderer()
+                dp = l.dataProvider()
+                newRenderer = None
+
+                extent = se.toCrs(l.crs())
+
+                assert isinstance(dp, QgsRasterDataProvider)
+                bands = None
+                if isinstance(r, QgsMultiBandColorRenderer):
+
+                    def getCE(band, ce):
+                        stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 500)
+                        ce = QgsContrastEnhancement(ce)
+                        ce.setMinimumValue(stats.minimumValue)
+                        ce.setMaximumValue(stats.maximumValue)
+                        return ce
+
+                    newRenderer = QgsMultiBandColorRenderer(None,r.redBand(), r.greenBand(), r.blueBand())
+                    newRenderer.setRedContrastEnhancement(getCE(r.redBand(), r.redContrastEnhancement()))
+                    newRenderer.setGreenContrastEnhancement(getCE(r.greenBand(), r.greenContrastEnhancement()))
+                    newRenderer.setBlueContrastEnhancement(getCE(r.blueBand(), r.blueContrastEnhancement()))
+
+                    results[self.tsdView.TSD.sensor] = newRenderer
+                elif isinstance(r, QgsSingleBandPseudoColorRenderer):
+                    newRenderer = r.clone()
+                    stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)
+                    shader = newRenderer.shader()
+                    newRenderer.setClassificationMax(stats.maximumValue)
+                    newRenderer.setClassificationMin(stats.minimumValue)
+                    shader.setMaximumValue(stats.maximumValue)
+                    shader.setMinimumValue(stats.minimumValue)
+                    s = ""
+
+                if newRenderer is not None:
+                    self.sensorView.setLayerRenderer(newRenderer)
+        s = ""
+
+    def activateMapTool(self, key):
+        if key is None:
+            self.setMapTool(None)
+        elif key in self.MAPTOOLS.keys():
+            self.setMapTool(self.MAPTOOLS[key])
+        else:
+            logger.error('unknown map tool key "{}"'.format(key))
+
+    def saveMapImageDialog(self, fileType):
+        lastDir = SETTINGS.value('CANVAS_SAVE_IMG_DIR', os.path.expanduser('~'))
+        path = jp(lastDir, '{}.{}.{}'.format(self.tsdView.TSD.date, self.mapView.title(), fileType.lower()))
+
+        path = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
+        if len(path) > 0:
+            self.saveAsImage(path, None, fileType)
+            SETTINGS.setValue('CANVAS_SAVE_IMG_DIR', os.path.dirname(path))
+
+
+    def setRenderer(self, renderer, targetLayerUri=None):
+        if targetLayerUri is None:
+            targetLayerUri = str(self.referenceLayer.source())
+
+        lyrs = [l for l in self.mapLayersToRender() if str(l.source()) == targetLayerUri]
+        assert len(lyrs) <= 1
+        for lyr in lyrs:
+            if isinstance(renderer, QgsMultiBandColorRenderer):
+                r = renderer.clone()
+                r.setInput(lyr.dataProvider())
+            elif isinstance(renderer, QgsSingleBandPseudoColorRenderer):
+                r = renderer.clone()
+                #r = QgsSingleBandPseudoColorRenderer(None, renderer.band(), None)
+                r.setInput(lyr.dataProvider())
+                cmin = renderer.classificationMin()
+                cmax = renderer.classificationMax()
+                r.setClassificationMin(cmin)
+                r.setClassificationMax(cmax)
+                #r.setShader(renderer.shader())
+                s = ""
+            else:
+                raise NotImplementedError()
+            lyr.setRenderer(r)
+
+        self.refresh()
+
+    def setSpatialExtent(self, spatialExtent):
+        assert isinstance(spatialExtent, SpatialExtent)
+        if self.spatialExtent() != spatialExtent:
+            self.blockSignals(True)
+            self.setDestinationCrs(spatialExtent.crs())
+            self.setExtent(spatialExtent)
+            self.blockSignals(False)
+            self.refresh()
+
+
+    def spatialExtent(self):
+        return SpatialExtent.fromMapCanvas(self)
+
+
+
+
+class CanvasBoundingBoxItem(QgsGeometryRubberBand):
+
+    def __init__(self, mapCanvas):
+        assert isinstance(mapCanvas, QgsMapCanvas)
+        super(CanvasBoundingBoxItem, self).__init__(mapCanvas)
+
+        self.canvas = mapCanvas
+        self.mCanvasExtents = dict()
+        self.mShow = True
+        self.mShowTitles = True
+        self.setIconType(QgsGeometryRubberBand.ICON_NONE)
+
+    def connectCanvas(self, canvas):
+        assert isinstance(canvas, QgsMapCanvas)
+        assert canvas != self.canvas
+        if canvas not in self.mCanvasExtents.keys():
+            self.mCanvasExtents[canvas] = None
+            canvas.extentsChanged.connect(lambda : self.onExtentsChanged(canvas))
+            canvas.destroyed.connect(lambda : self.disconnectCanvas(canvas))
+            self.onExtentsChanged(canvas)
+
+    def disconnectCanvas(self, canvas):
+            self.mCanvasExtents.pop(canvas)
+
+    def onExtentsChanged(self, canvas):
+        assert isinstance(canvas, QgsMapCanvas)
+
+        ext = SpatialExtent.fromMapCanvas(canvas)
+        ext = ext.toCrs(self.canvas.mapSettings().destinationCrs())
+
+        geom = QgsPolygonV2()
+        assert geom.fromWkt(ext.asWktPolygon())
+
+        self.mCanvasExtents[canvas] = (ext, geom)
+        self.refreshExtents()
+
+    def refreshExtents(self):
+        multi = QgsMultiPolygonV2()
+        if self.mShow:
+            for canvas, t in self.mCanvasExtents.items():
+                ext, geom = t
+                multi.addGeometry(geom.clone())
+        self.setGeometry(multi)
+
+    def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
+        super(CanvasBoundingBoxItem, self).paint(painter)
+
+        if self.mShowTitles and self.mShow:
+            painter.setPen(Qt.blue);
+            painter.setFont(QFont("Arial", 30))
+
+            for canvas, t in self.mCanvasExtents.items():
+                ext, geom = t
+                ULpx = self.toCanvasCoordinates(ext.center())
+                txt = canvas.windowTitle()
+                painter.drawLine(0, 0, 200, 200);
+                painter.drawText(ULpx,  txt)
+
+
+    def setShow(self, b):
+        assert isinstance(b, bool)
+        self.mShow = b
+
+    def setShowTitles(self, b):
+        assert isinstance(b, bool)
+        self.mShowTitles = b
+
+if __name__ == '__main__':
+    import site, sys
+    #add site-packages to sys.path as done by enmapboxplugin.py
+
+    from timeseriesviewer import sandbox
+    qgsApp = sandbox.initQgisEnvironment()
+
+
+
+    import example.Images
+    lyr1 = QgsRasterLayer(example.Images.Img_2012_05_09_LE72270652012130EDC00_BOA)
+    lyr2 = QgsRasterLayer(example.Images.Img_2012_05_09_LE72270652012130EDC00_BOA)
+    lyr3 = QgsRasterLayer(example.Images.Img_2012_05_09_LE72270652012130EDC00_BOA)
+
+    QgsMapLayerRegistry.instance().addMapLayers([lyr1, lyr2, lyr3])
+
+    w = QWidget()
+    l = QHBoxLayout()
+    canvas1 = QgsMapCanvas()
+    canvas1.setWindowTitle('Canvas1')
+    canvas1.setLayerSet([QgsMapCanvasLayer(lyr1)])
+    canvas1.setExtent(lyr1.extent())
+    canvas2 = QgsMapCanvas()
+    canvas2.setWindowTitle('Canvas2')
+    canvas2.setLayerSet([QgsMapCanvasLayer(lyr2)])
+    canvas2.setExtent(lyr2.extent())
+    canvas3 = QgsMapCanvas()
+    canvas3.setWindowTitle('Canvas3')
+    #canvas3.setLayerSet([QgsMapCanvasLayer(lyr3)])
+    #canvas3.setExtent(lyr3.extent())
+
+    item = CanvasBoundingBoxItem(canvas1)
+    item.setShowTitles(True)
+    item.connectCanvas(canvas2)
+    item.connectCanvas(canvas3)
+
+    l.addWidget(canvas1)
+    l.addWidget(canvas2)
+    l.addWidget(canvas3)
+    w.setLayout(l)
+
+    w.show()
+
+    qgsApp.exec_()
+    qgsApp.exitQgis()
diff --git a/timeseriesviewer/maptools.py b/timeseriesviewer/maptools.py
index 3aa2d5a4daf71ec490314879ee3ba58f40fc369e..2823c389bae2e3e12cfd0113b1492ab44033c4fa 100644
--- a/timeseriesviewer/maptools.py
+++ b/timeseriesviewer/maptools.py
@@ -31,91 +31,6 @@ class CursorLocationValueMapTool(QgsMapTool):
         self.sigLocationRequest.emit(point, crs)
 
 
-def add_QgsRasterLayer(iface, path, rgb):
-    if iface:
-
-        fi = QFileInfo(path)
-        layer = QgsRasterLayer(path, fi.baseName())
-        if not layer.isValid():
-            six.print_('Failed to load {}'.format(path))
-        else:
-            rasterLyr = iface.addRasterLayer(path, fi.baseName())
-
-
-            renderer = rasterLyr.renderer()
-            print(type(renderer))
-
-            if type(renderer) is QgsMultiBandColorRenderer:
-                renderer.setRedBand(rgb[0])
-                renderer.setGreenBand(rgb[0])
-                renderer.setBlueBand(rgb[0])
-
-        if hasattr(layer, "triggerRepaint"):
-            #layer.repaintRequested()
-            layer.triggerRepaint()
-
-
-    s = ""
-
-
-paste_test = """
-    <!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
-<qgis version="2.12.3-Lyon">
- <pipe>
-  <rasterrenderer opacity="1" alphaBand="-1" blueBand="1" greenBand="2" type="multibandcolor" redBand="3">
-   <rasterTransparency/>
-   <redContrastEnhancement>
-    <minValue>2803.02</minValue>
-    <maxValue>6072.21</maxValue>
-    <algorithm>StretchToMinimumMaximum</algorithm>
-   </redContrastEnhancement>
-   <greenContrastEnhancement>
-    <minValue>5103.86</minValue>
-    <maxValue>7228.58</maxValue>
-    <algorithm>StretchToMinimumMaximum</algorithm>
-   </greenContrastEnhancement>
-   <blueContrastEnhancement>
-    <minValue>5992.32</minValue>
-    <maxValue>7718.33</maxValue>
-    <algorithm>StretchToMinimumMaximum</algorithm>
-   </blueContrastEnhancement>
-  </rasterrenderer>
-  <brightnesscontrast brightness="0" contrast="0"/>
-  <huesaturation colorizeGreen="128" colorizeOn="0" colorizeRed="255" colorizeBlue="128" grayscaleMode="0" saturation="0" colorizeStrength="100"/>
-  <rasterresampler maxOversampling="2"/>
- </pipe>
- <blendMode>0</blendMode>
-</qgis>
-    """
-
-def paste_band_settings(txt):
-
-    result = None
-    try:
-        import xml.etree.ElementTree as ET
-        tree = ET.fromstring(txt)
-
-        renderer = tree.find('*/rasterrenderer')
-        if renderer is not None:
-            bands = list()
-            ranges = list()
-            for c in ['red','green','blue']:
-                name = c + 'Band'
-                if name not in renderer.attrib.keys():
-                    return result
-
-                bands.append(int(renderer.attrib[name]))
-                v_min = float(renderer.find(c+'ContrastEnhancement/minValue').text)
-                v_max = float(renderer.find(c+'ContrastEnhancement/maxValue').text)
-                ranges.append((v_min, v_max))
-
-            result = (bands, ranges)
-    except:
-        pass
-
-    return result
-
-
 class PointMapTool(QgsMapToolEmitPoint):
 
     sigCoordinateSelected = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
@@ -315,13 +230,3 @@ class RectangleMapTool(QgsMapToolEmitPoint):
     #   super(RectangleMapTool, self).deactivate()
     #self.deactivated.emit()
 
-
-
-def tests():
-
-    print(paste_band_settings(paste_test))
-    print(paste_band_settings('foo'))
-
-if __name__ == '__main__':
-    tests()
-    print('Done')
\ No newline at end of file
diff --git a/timeseriesviewer/profilevisualization.py b/timeseriesviewer/profilevisualization.py
index d51578ab2255da61755eaaac4013f7b9d72f9f85..e454ea83ab4f6628717be2b2bce09e1ba62c0252 100644
--- a/timeseriesviewer/profilevisualization.py
+++ b/timeseriesviewer/profilevisualization.py
@@ -87,6 +87,9 @@ class PixelLoadWorker(QObject):
             self.recentFile = path
 
             lyr = QgsRasterLayer(path)
+            if not lyr.isValid():
+                logger.debug('Layer not valid: {}'.format(path))
+                continue
             dp = lyr.dataProvider()
 
             trans = QgsCoordinateTransform(crs, dp.crs())
diff --git a/timeseriesviewer/sandbox.py b/timeseriesviewer/sandbox.py
index 2de357c53278eda395cd0acec1390613a48afe04..7333b9b78440e4486e05984b595c509d0519e87c 100644
--- a/timeseriesviewer/sandbox.py
+++ b/timeseriesviewer/sandbox.py
@@ -1,7 +1,8 @@
 from __future__ import absolute_import
 import six, sys, os, gc, re, collections, site, inspect
+import logging, io
+logger = logging.getLogger(__name__)
 from osgeo import gdal, ogr
-
 from qgis import *
 from qgis.core import *
 from qgis.gui import *
@@ -9,6 +10,9 @@ from PyQt4.QtGui import *
 from PyQt4.QtCore import *
 
 from timeseriesviewer import *
+from timeseriesviewer.utils import *
+from timeseriesviewer import file_search
+from timeseriesviewer.timeseries import *
 
 class SandboxObjects(object):
 
@@ -33,40 +37,9 @@ def sandboxGui():
     S.ui.show()
     S.run()
 
-    if False:
-        from timeseriesviewer import file_search
-        searchDir = r'H:\LandsatData\Landsat_NovoProgresso'
-        files = file_search(searchDir, '*band4.img', recursive=True)
-
-        #searchDir = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\01_UncutVRT'
-        #files = file_search(searchDir, '*BOA.vrt', recursive=True)
-
-        files = files[0:10]
-        S.loadImageFiles(files)
-        return
-    if False:
-        files = [r'E:\_EnMAP\temp\temp_bj\landsat\37S\EB\LC81720342015129LGN00\LC81720342015129LGN00_sr.tif']
-        S.loadImageFiles(files)
-        return
-    if True:
-        from timeseriesviewer import file_search
-        files = file_search(r'E:\_EnMAP\temp\temp_bj\landsat\37S\EB', '*_sr.tif', recursive=True)
-        #files = files[0:15]
-        print('Load {} images...'.format(len(files)))
-        S.loadImageFiles(files)
-        return
-    if False:
-        files = [r'H:\\LandsatData\\Landsat_NovoProgresso\\LC82270652013140LGN01\\LC82270652013140LGN01_sr_band4.img']
-        S.loadImageFiles(files)
-        return
-    if False:
-        S.spatialTemporalVis.MVC.createMapView()
-        S.loadTimeSeries(path=PATH_EXAMPLE_TIMESERIES, n_max=1)
-        return
-    if True:
-        S.loadTimeSeries(path=PATH_EXAMPLE_TIMESERIES, n_max=100)
-        return
-    pass
+    S.spatialTemporalVis.MVC.createMapView()
+    import example.Images
+    S.addTimeSeriesImages([example.Images.Img_2014_07_10_LC82270652014191LGN00_BOA])
 
 class QgisFake(QgisInterface):
 
@@ -219,15 +192,11 @@ def gdal_qgis_benchmark():
     s =""
 
 
-
-if __name__ == '__main__':
-    import site, sys
-    #add site-packages to sys.path as done by enmapboxplugin.py
+def initQgisEnvironment():
 
     from timeseriesviewer import DIR_SITE_PACKAGES
     site.addsitedir(DIR_SITE_PACKAGES)
-
-    #prepare QGIS environment
+    # prepare QGIS environment
     if sys.platform == 'darwin':
         PATH_QGS = r'/Applications/QGIS.app/Contents/MacOS'
         os.environ['GDAL_DATA'] = r'/usr/local/Cellar/gdal/1.11.3_1/share'
@@ -235,19 +204,25 @@ if __name__ == '__main__':
         # assume OSGeo4W startup
         PATH_QGS = os.environ['QGIS_PREFIX_PATH']
     assert os.path.exists(PATH_QGS)
-
     qgsApp = QgsApplication([], True)
     QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns')
     QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns/qgis')
     qgsApp.setPrefixPath(PATH_QGS, True)
     qgsApp.initQgis()
+    return qgsApp
+
+
+
+if __name__ == '__main__':
+    import site, sys
+    #add site-packages to sys.path as done by enmapboxplugin.py
+
+    qgsApp = initQgisEnvironment()
 
     #run tests
     if False: gdal_qgis_benchmark()
     if False: sandboxQgisBridge()
     if True: sandboxGui()
-    if False: test_component()
-
 
     #close QGIS
     qgsApp.exec_()
diff --git a/timeseriesviewer/tests.py b/timeseriesviewer/tests.py
new file mode 100644
index 0000000000000000000000000000000000000000..c34bde98ef158ce732d19a13791b59bbf1ee1b33
--- /dev/null
+++ b/timeseriesviewer/tests.py
@@ -0,0 +1,83 @@
+
+import os, re, io
+from unittest import TestCase
+from osgeo import gdal
+from qgis import *
+#from timeseriesviewer import *
+from . import *
+from timeseries import *
+
+class TestFileFormatLoading(TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        cls.TS = TimeSeries()
+
+        if False:
+            cls.savedStdOut = sys.stdout
+            cls.savedStdIn = sys.stdin
+
+            cls.stdout = io.StringIO()
+            cls.stderr = io.StringIO()
+            sys.stdout = cls.stdout
+            sys.stderr = cls.stderr
+
+    @classmethod
+    def tearDownClass(cls):
+        pass
+        #sys.stdout = cls.stdout
+        #sys.stderr = cls.stderr
+
+    def setUp(self):
+        self.TS.clear()
+
+    def tearDown(self):
+        self.TS.clear()
+
+
+
+    def test_loadLandsat(self):
+        searchDir = jp(DIR_EXAMPLES, 'Images')
+        files = file_search(searchDir, '*_L*_BOA.bsq')[0:3]
+        self.TS.addFiles(files)
+
+        self.assertEqual(len(files), len(self.TS))
+        s = ""
+
+    def test_nestedVRTs(self):
+        # load VRTs pointing to another VRT pointing to Landsat imagery
+        searchDir = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\02_CuttedVRT'
+        files = file_search(searchDir, '*BOA.vrt', recursive=True)[0:3]
+        self.TS.addFiles(files)
+        self.assertEqual(len(files), len(self.TS))
+
+    def test_loadRapidEye(self):
+        # load RapidEye
+        searchDir = r'H:\RapidEye\3A'
+        files = file_search(searchDir, '*.tif', recursive=True)
+        files = [f for f in files if not re.search('_(udm|browse)\.tif$', f)]
+        self.TS.addFiles(files)
+        self.assertEqual(len(files), len(self.TS))
+
+    def test_loadPleiades(self):
+        #load Pleiades data
+        searchDir = r'H:\Pleiades'
+        #files = file_search(searchDir, 'DIM*.xml', recursive=True)
+        files = file_search(searchDir, '*.jp2', recursive=True)[0:3]
+        self.TS.addFiles(files)
+        self.assertEqual(len(files), len(self.TS))
+
+    def test_loadSentinel2(self):
+        #load Sentinel-2
+        searchDir = r'H:\Sentinel2'
+        files = file_search(searchDir, '*MSIL1C.xml', recursive=True)
+        self.TS.addFiles(files)
+
+        #self.assertRegexpMatches(self.stderr.getvalue().strip(), 'Unable to add:')
+        self.assertEqual(0, len(self.TS))  # do not add a containers
+        subdatasets = []
+        for file in files:
+            subs = gdal.Open(file).GetSubDatasets()
+            subdatasets.extend(s[0] for s in subs)
+        self.TS.addFiles(subdatasets)
+        self.assertEqual(len(subdatasets), len(self.TS))  # add subdatasets
diff --git a/timeseriesviewer/timeseries.py b/timeseriesviewer/timeseries.py
index b026ce9bca4011b0b6b21d44656727fad86e8917..7f80e9ff9138b82baf2e2d600e0fc9b962669f61 100644
--- a/timeseriesviewer/timeseries.py
+++ b/timeseriesviewer/timeseries.py
@@ -1,6 +1,7 @@
 from __future__ import absolute_import
 import six, sys, os, gc, re, collections, site, inspect, time, traceback, copy
-
+import logging
+logger = logging.getLogger(__name__)
 import bisect, datetime
 from osgeo import gdal, ogr
 
@@ -11,11 +12,15 @@ from PyQt4.QtGui import *
 from PyQt4.QtCore import *
 from PyQt4.QtXml import *
 
-from osgeo import gdal, ogr
+from osgeo import gdal, ogr, gdal_array
+
+gdal.SetConfigOption('VRT_SHARED_SOURCE', '0') #!important. really. do not change this.
+
 import numpy as np
 
-from timeseriesviewer import DIR_REPO, DIR_EXAMPLES, dprint, jp, findAbsolutePath
+from timeseriesviewer import DIR_REPO, DIR_EXAMPLES, jp
 from timeseriesviewer.dateparser import parseDateFromDataSet
+
 def transformGeometry(geom, crsSrc, crsDst, trans=None):
     if trans is None:
         assert isinstance(crsSrc, QgsCoordinateReferenceSystem)
@@ -47,15 +52,6 @@ def convertMetricUnit(value, u1, u2):
 
     return value * 10**(e1-e2)
 
-def verifyVRT(pathVRT):
-
-    ds = gdal.Open(pathVRT)
-    if ds is None:
-        return False
-    s = ""
-
-    return True
-
 
 def getDS(pathOrDataset):
     if isinstance(pathOrDataset, gdal.Dataset):
@@ -183,21 +179,24 @@ class SensorInstrument(QObject):
 
 
 def verifyInputImage(path, vrtInspection=''):
-
-    if not os.path.exists(path):
-        print('{}Image does not exist: '.format(vrtInspection, path))
-        return False
+    if path is None or not type(path) in [str, unicode]:
+        return None
     ds = gdal.Open(path)
+
     if not ds:
-        print('{}GDAL unable to open: '.format(vrtInspection, path))
+        logger.error('{}GDAL unable to open: '.format(vrtInspection, path))
+        return False
+
+    if ds.RasterCount == 0 and len(ds.GetSubDatasets()) > 0:
+        logger.error('Can not open container {}.\nPlease specify a subdataset'.format(path))
         return False
     if ds.GetDriver().ShortName == 'VRT':
         vrtInspection = 'VRT Inspection {}\n'.format(path)
-        validSrc = [verifyInputImage(p, vrtInspection=vrtInspection) for p in set(ds.GetFileList()) - set([path])]
+        nextFiles = set(ds.GetFileList()) - set([path])
+        validSrc = [verifyInputImage(p, vrtInspection=vrtInspection) for p in nextFiles]
         if not all(validSrc):
             return False
-    else:
-        return True
+    return True
 
 def pixel2coord(gt, x, y):
     """Returns global coordinates from pixel x, y coords"""
@@ -215,12 +214,12 @@ class TimeSeriesDatum(QObject):
         :param path:
         :return:
         """
-        p = findAbsolutePath(path)
+
         tsd = None
-        if verifyInputImage(p):
+        if verifyInputImage(path):
 
             try:
-                tsd =TimeSeriesDatum(None, p)
+                tsd = TimeSeriesDatum(None, path)
             except :
                 pass
 
@@ -241,7 +240,7 @@ class TimeSeriesDatum(QObject):
 
         ds = getDS(pathImg)
 
-        self.pathImg = ds.GetFileList()[0]
+        self.pathImg = ds.GetFileList()[0] if isinstance(pathImg, gdal.Dataset) else pathImg
 
         self.timeSeries = timeSeries
         self.nb, self.nl, self.ns, self.crs, px_x, px_y = getSpatialPropertiesFromDataset(ds)
@@ -460,7 +459,7 @@ class TimeSeries(QObject):
                     TSD.sensor = existingSensors[existingSensors.index(TSD.sensor)]
 
                 if TSD in self.data:
-                    six.print_('Time series datum already added: {} {}'.format(str(TSD), TSD.pathImg), file=sys.stderr)
+                    six.print_('Time series date-time already added ({} {}). \nPlease use VRTs to mosaic images with same acquisition date-time.'.format(str(TSD), TSD.pathImg), file=sys.stderr)
                 else:
                     self.Sensors[TSD.sensor].append(TSD)
                     #insert sorted
@@ -484,6 +483,8 @@ class TimeSeries(QObject):
 
     def addFiles(self, files):
         assert isinstance(files, list)
+        files = [f for f in files if f is not None]
+
         nMax = len(files)
         nDone = 0
         self.sigLoadingProgress.emit(0,nMax, 'Start loading {} files...'.format(nMax))
@@ -493,7 +494,7 @@ class TimeSeries(QObject):
             tsd = TimeSeriesDatum.createFromPath(file)
             if tsd is None:
                 msg = 'Unable to add: {}'.format(os.path.basename(file))
-                dprint(msg, file=sys.stderr)
+                logger.error(msg)
             else:
                 self.addTimeSeriesDates([tsd])
                 msg = 'Added {}'.format(os.path.basename(file))
diff --git a/timeseriesviewer/ui/__init__.py b/timeseriesviewer/ui/__init__.py
index a1644e95ca173dc47c0071cb0f839c11da8b14dd..5614176378901e965ade31b5ce8b8f0058556c8c 100644
--- a/timeseriesviewer/ui/__init__.py
+++ b/timeseriesviewer/ui/__init__.py
@@ -1,4 +1,5 @@
-import os, sys, six, importlib
+import os, sys, six, importlib, logging
+logger = logging.getLogger(__name__)
 from PyQt4.QtCore import *
 from PyQt4.QtXml import *
 from PyQt4.QtGui import *
@@ -8,7 +9,7 @@ from PyQt4 import uic
 #dictionary to store form classes. *.ui file name is key
 FORM_CLASSES = dict()
 
-from timeseriesviewer import jp, DIR_UI, dprint
+from timeseriesviewer import jp, DIR_UI
 
 
 def loadUIFormClass(pathUi, from_imports=False):
@@ -65,3 +66,4 @@ def loadUIFormClass(pathUi, from_imports=False):
         FORM_CLASSES[pathUi] = formClass
 
     return FORM_CLASSES[pathUi]
+
diff --git a/timeseriesviewer/ui/crosshairwidget.ui b/timeseriesviewer/ui/crosshairwidget.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8abf94794aa6bd2899b763ba8cdb6173123a3007
--- /dev/null
+++ b/timeseriesviewer/ui/crosshairwidget.ui
@@ -0,0 +1,242 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Form</class>
+ <widget class="QWidget" name="Form">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>234</width>
+    <height>179</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="1" column="1">
+    <widget class="QSpinBox" name="spinBoxCrosshairThickness">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="suffix">
+      <string> px</string>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="2">
+    <widget class="QLabel" name="label_13">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Alpha</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="0">
+    <widget class="QCheckBox" name="cbCrosshairShowDot">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Box</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="4">
+    <spacer name="horizontalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>40</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="3" column="1">
+    <widget class="QSpinBox" name="spinBoxCrosshairGap">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="suffix">
+      <string> %</string>
+     </property>
+     <property name="prefix">
+      <string/>
+     </property>
+     <property name="maximum">
+      <number>100</number>
+     </property>
+     <property name="singleStep">
+      <number>5</number>
+     </property>
+     <property name="value">
+      <number>10</number>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QgsColorButton" name="btnCrosshairColor">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="colorDialogTitle">
+      <string>Select Map Canvas Background Color</string>
+     </property>
+     <property name="color">
+      <color>
+       <red>255</red>
+       <green>0</green>
+       <blue>0</blue>
+      </color>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="3">
+    <widget class="QSlider" name="spinBoxCrosshairAlpha">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="maximum">
+      <number>255</number>
+     </property>
+     <property name="value">
+      <number>255</number>
+     </property>
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="7" column="0">
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="0" column="0">
+    <widget class="QLabel" name="label_14">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Color</string>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QSpinBox" name="spinBoxDotSize">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="suffix">
+      <string> px</string>
+     </property>
+     <property name="minimum">
+      <number>1</number>
+     </property>
+     <property name="maximum">
+      <number>100</number>
+     </property>
+     <property name="singleStep">
+      <number>5</number>
+     </property>
+     <property name="value">
+      <number>1</number>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Thickness</string>
+     </property>
+    </widget>
+   </item>
+   <item row="3" column="0">
+    <widget class="QLabel" name="label_3">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Gap</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Size</string>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="1">
+    <widget class="QSpinBox" name="spinBoxCrosshairSize">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="suffix">
+      <string> %</string>
+     </property>
+     <property name="maximum">
+      <number>100</number>
+     </property>
+     <property name="singleStep">
+      <number>5</number>
+     </property>
+     <property name="value">
+      <number>95</number>
+     </property>
+    </widget>
+   </item>
+   <item row="6" column="0" colspan="2">
+    <widget class="QCheckBox" name="cbShowPixelBoundaries">
+     <property name="text">
+      <string>Pixel boundaries</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="2" rowspan="5" colspan="2">
+    <widget class="QgsMapCanvas" name="mapCanvas">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QgsColorButton</class>
+   <extends>QPushButton</extends>
+   <header>qgscolorbutton.h</header>
+  </customwidget>
+  <customwidget>
+   <class>QgsMapCanvas</class>
+   <extends>QGraphicsView</extends>
+   <header>qgis.gui</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/timeseriesviewer/ui/docks.py b/timeseriesviewer/ui/docks.py
index 0fd851c0a0688c53ac4f55bcead11890c6660d77..746ef7147c667d735e1abf0c0fedf30b6a8811ef 100644
--- a/timeseriesviewer/ui/docks.py
+++ b/timeseriesviewer/ui/docks.py
@@ -37,6 +37,9 @@ class TsvDockWidgetBase(QgsDockWidget):
 
 
 class RenderingDockUI(TsvDockWidgetBase, load('renderingdock.ui')):
+    from timeseriesviewer.crosshair import CrosshairStyle
+
+    sigMapCanvasColorChanged = pyqtSignal(QColor)
     def __init__(self, parent=None):
         super(RenderingDockUI, self).__init__(parent)
         self.setupUi(self)
@@ -50,14 +53,12 @@ class RenderingDockUI(TsvDockWidgetBase, load('renderingdock.ui')):
 
         self.subsetSizeWidgets = [self.spinBoxSubsetSizeX, self.spinBoxSubsetSizeY]
 
-        self.gbCrosshair.setVisible(False)
-
-
 
 
     def subsetSize(self):
         return QSize(self.spinBoxSubsetSizeX.value(),
                      self.spinBoxSubsetSizeY.value())
+
     def onSubsetValueChanged(self, key):
         if self.checkBoxKeepSubsetAspectRatio.isChecked():
 
@@ -355,41 +356,9 @@ if __name__ == '__main__':
     import site, sys
     #add site-packages to sys.path as done by enmapboxplugin.py
 
-    from timeseriesviewer import DIR_SITE_PACKAGES
-    site.addsitedir(DIR_SITE_PACKAGES)
-
-    #prepare QGIS environment
-    if sys.platform == 'darwin':
-        PATH_QGS = r'/Applications/QGIS.app/Contents/MacOS'
-        os.environ['GDAL_DATA'] = r'/usr/local/Cellar/gdal/1.11.3_1/share'
-    else:
-        # assume OSGeo4W startup
-        PATH_QGS = os.environ['QGIS_PREFIX_PATH']
-    assert os.path.exists(PATH_QGS)
-
-    qgsApp = QgsApplication([], True)
-    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns')
-    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns/qgis')
-    qgsApp.setPrefixPath(PATH_QGS, True)
-    qgsApp.initQgis()
-
-    #run tests
-    #d = AboutDialogUI()
-    #d.show()
-
-    from timeseriesviewer.tests import *
-
-    TS = TestObjects.TimeSeries()
-    ext = TS.getMaxSpatialExtent()
-
-    d = ProfileViewDockUI()
-    d.connectTimeSeries(TS)
+    from timeseriesviewer import sandbox
+    qgsApp = sandbox.initQgisEnvironment()
+    d = RenderingDockUI()
     d.show()
-    d.loadCoordinate(ext.center(), ext.crs())
-
-    #close QGIS
-    try:
-        qgsApp.exec_()
-    except:
-        s = ""
+    qgsApp.exec_()
     qgsApp.exitQgis()
diff --git a/timeseriesviewer/ui/icons/crosshair.png b/timeseriesviewer/ui/icons/crosshair.png
new file mode 100644
index 0000000000000000000000000000000000000000..c611eebc36098005e8d93155104be82b246664b4
Binary files /dev/null and b/timeseriesviewer/ui/icons/crosshair.png differ
diff --git a/timeseriesviewer/ui/icons/crosshair.svg b/timeseriesviewer/ui/icons/crosshair.svg
new file mode 100644
index 0000000000000000000000000000000000000000..928c51c33733483052d494c73726ae27a97778fa
--- /dev/null
+++ b/timeseriesviewer/ui/icons/crosshair.svg
@@ -0,0 +1,532 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="24"
+   height="24"
+   id="svg5692"
+   version="1.1"
+   inkscape:version="0.91 r13725"
+   sodipodi:docname="crosshair.svg"
+   inkscape:export-filename="D:\Repositories\QGIS_Plugins\hub-timeseriesviewer\timeseriesviewer\ui\icons\crosshair.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90"
+   style="display:inline">
+  <title
+     id="title2901">zoom 1 to 1</title>
+  <defs
+     id="defs5694">
+    <linearGradient
+       id="linearGradient3657">
+      <stop
+         style="stop-color:#fce94f;stop-opacity:1;"
+         offset="0"
+         id="stop3659" />
+      <stop
+         style="stop-color:#e7ce04;stop-opacity:1;"
+         offset="1"
+         id="stop3661" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2877">
+      <stop
+         style="stop-color:#edd400;stop-opacity:1;"
+         offset="0"
+         id="stop2879" />
+      <stop
+         style="stop-color:#c2ad00;stop-opacity:1;"
+         offset="1"
+         id="stop2881" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient4042">
+      <stop
+         style="stop-color:#f2d6a9;stop-opacity:1;"
+         offset="0"
+         id="stop4044" />
+      <stop
+         style="stop-color:#e9b96e;stop-opacity:1;"
+         offset="1"
+         id="stop4046" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2843">
+      <stop
+         style="stop-color:#eeeeec;stop-opacity:1;"
+         offset="0"
+         id="stop2845" />
+      <stop
+         style="stop-color:#c8c8c2;stop-opacity:1;"
+         offset="1"
+         id="stop2847" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient2835">
+      <stop
+         style="stop-color:#ccf2a6;stop-opacity:1;"
+         offset="0"
+         id="stop2837" />
+      <stop
+         style="stop-color:#8ae234;stop-opacity:1;"
+         offset="1"
+         id="stop2839" />
+    </linearGradient>
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 16 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="32 : 16 : 1"
+       inkscape:persp3d-origin="16 : 10.666667 : 1"
+       id="perspective3257" />
+    <inkscape:perspective
+       id="perspective6979"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective7934"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective8023"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective8057"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective8095"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective8219"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective8279"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3803"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3869"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3929"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3968"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4002"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4032"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective4053"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2905"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2979"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2842"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective2978"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <inkscape:perspective
+       id="perspective3238"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4048"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       gradientUnits="userSpaceOnUse" />
+    <inkscape:perspective
+       id="perspective4058"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042-2"
+       id="radialGradient4048-2"
+       cx="8.5770311"
+       cy="3.8663561"
+       fx="8.5770311"
+       fy="3.8663561"
+       r="6.1587391"
+       gradientTransform="matrix(0.81185454,1.1365964,-1.1707271,0.83623306,20.063146,-1.4817979)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       id="linearGradient4042-2">
+      <stop
+         style="stop-color:#f2d6a9;stop-opacity:1;"
+         offset="0"
+         id="stop4044-8" />
+      <stop
+         style="stop-color:#e9b96e;stop-opacity:1;"
+         offset="1"
+         id="stop4046-4" />
+    </linearGradient>
+    <radialGradient
+       r="6.1587391"
+       fy="17.838446"
+       fx="0.5"
+       cy="17.838446"
+       cx="0.5"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-8.4170292)"
+       gradientUnits="userSpaceOnUse"
+       id="radialGradient4067"
+       xlink:href="#linearGradient4042-2"
+       inkscape:collect="always" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042-2"
+       id="radialGradient4094"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.81185454,1.1365964,-1.1707271,0.83623306,20.063146,-1.4817979)"
+       cx="8.5770311"
+       cy="3.8663561"
+       fx="8.5770311"
+       fy="3.8663561"
+       r="6.1587391" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042-2"
+       id="radialGradient4097"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.81185454,1.1365964,-1.1707271,0.83623306,20.063146,-1.4817979)"
+       cx="8.5770311"
+       cy="3.8663561"
+       fx="8.5770311"
+       fy="3.8663561"
+       r="6.1587391" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042-2"
+       id="radialGradient4100"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.81185454,1.1365964,-1.1707271,0.83623306,20.063146,-1.4817979)"
+       cx="8.5770311"
+       cy="3.8663561"
+       fx="8.5770311"
+       fy="3.8663561"
+       r="6.1587391" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042-2"
+       id="radialGradient4103"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.81185454,1.1365964,-1.1707271,0.83623306,20.063146,-1.4817979)"
+       cx="8.5770311"
+       cy="3.8663561"
+       fx="8.5770311"
+       fy="3.8663561"
+       r="6.1587391" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4106"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4109"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4112"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4115"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4042"
+       id="radialGradient4118"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8118545,0.97422537,-1.1052481,0.9210397,19.809981,-0.4170292)"
+       cx="0.5"
+       cy="17.838446"
+       fx="0.5"
+       fy="17.838446"
+       r="6.158739" />
+    <inkscape:perspective
+       id="perspective8198"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3657"
+       id="linearGradient3663"
+       x1="10.5"
+       y1="10.5"
+       x2="13.5"
+       y2="18.5"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3657"
+       id="linearGradient3669"
+       gradientUnits="userSpaceOnUse"
+       x1="10.5"
+       y1="10.5"
+       x2="13.5"
+       y2="18.5"
+       gradientTransform="translate(0,-3)" />
+    <inkscape:perspective
+       id="perspective4821"
+       inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
+       inkscape:vp_z="1 : 0.5 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="0 : 0.5 : 1"
+       sodipodi:type="inkscape:persp3d" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="15.99"
+     inkscape:cx="12.676896"
+     inkscape:cy="19.181199"
+     inkscape:current-layer="g4212"
+     showgrid="true"
+     inkscape:grid-bbox="true"
+     inkscape:document-units="px"
+     borderlayer="false"
+     inkscape:window-width="1280"
+     inkscape:window-height="962"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:snap-bbox="true"
+     inkscape:snap-grids="false"
+     inkscape:snap-object-midpoints="true">
+    <inkscape:grid
+       type="xygrid"
+       id="grid5700"
+       empspacing="5"
+       visible="true"
+       enabled="true"
+       snapvisiblegridlinesonly="true"
+       dotted="true"
+       originx="2.5px"
+       originy="2.5px" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5697">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title>zoom 1 to 1</dc:title>
+        <dc:date>2011-03-11</dc:date>
+        <dc:creator>
+          <cc:Agent>
+            <dc:title>Robert Szczepanek</dc:title>
+          </cc:Agent>
+        </dc:creator>
+        <dc:rights>
+          <cc:Agent>
+            <dc:title>Robert Szczepanek</dc:title>
+          </cc:Agent>
+        </dc:rights>
+        <dc:subject>
+          <rdf:Bag>
+            <rdf:li>icon</rdf:li>
+            <rdf:li>gis</rdf:li>
+          </rdf:Bag>
+        </dc:subject>
+        <dc:coverage>GIS icons 0.2</dc:coverage>
+        <cc:license
+           rdf:resource="http://creativecommons.org/licenses/by-sa/3.0/" />
+      </cc:Work>
+      <cc:License
+         rdf:about="http://creativecommons.org/licenses/by-sa/3.0/">
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Reproduction" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#Distribution" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#Notice" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#Attribution" />
+        <cc:permits
+           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
+        <cc:requires
+           rdf:resource="http://creativecommons.org/ns#ShareAlike" />
+      </cc:License>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer4"
+     inkscape:label="1"
+     style="display:inline"
+     transform="translate(0,-8)">
+    <path
+       inkscape:connector-curvature="0"
+       sodipodi:nodetypes="czzzz"
+       id="path3838"
+       d="m 10.434781,12.175305 c 2.086957,-3.1302127 4.635391,-3.545545 6.260871,-2.08681 1.62548,1.458739 -2.086957,1.043405 -4.173914,3.130215 -2.086957,2.086806 0,6.260424 -2.086957,6.260424 -2.0869553,0 -2.0869553,-4.173618 0,-7.303829 z"
+       style="opacity:0.7;fill:#fcffff;fill-rule:evenodd;stroke:none;display:inline" />
+    <g
+       id="g4212"
+       transform="translate(-3.6898061,2.0950594)">
+      <path
+         sodipodi:nodetypes="cc"
+         inkscape:connector-curvature="0"
+         id="path5046"
+         d="m 15.130435,4.192698 0,28.166698"
+         style="fill:none;fill-rule:evenodd;stroke:#e40f0f;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-dashoffset:0.89999998;stroke-opacity:1" />
+      <path
+         style="fill:none;fill-rule:evenodd;stroke:#e40f0f;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-dashoffset:0.89999998;stroke-opacity:1"
+         d="m 29.213784,18.276047 -28.166698,0"
+         id="path4210"
+         inkscape:connector-curvature="0"
+         sodipodi:nodetypes="cc" />
+      <rect
+         style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1"
+         id="rect4198-0"
+         width="7.8173862"
+         height="7.8173862"
+         x="11.221742"
+         y="14.367353" />
+      <rect
+         style="fill:#e40f0f;fill-opacity:1;stroke:#e40f0f;stroke-width:0.12808283;stroke-linecap:round;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1"
+         id="rect4198"
+         width="1.0392317"
+         height="1.0595345"
+         x="14.61082"
+         y="17.746279" />
+    </g>
+  </g>
+</svg>
diff --git a/timeseriesviewer/ui/labelingdock.ui b/timeseriesviewer/ui/labelingdock.ui
index a7326633874fc3674c96bc28a3175a3201826180..e81ca1a1f8cea74dc9c0490eea7cbb4b81cd62ee 100644
--- a/timeseriesviewer/ui/labelingdock.ui
+++ b/timeseriesviewer/ui/labelingdock.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>400</width>
-    <height>256</height>
+    <height>261</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -76,6 +76,16 @@
       </item>
      </layout>
     </item>
+    <item>
+     <widget class="QCheckBox" name="checkBox">
+      <property name="text">
+       <string>Edit layer</string>
+      </property>
+     </widget>
+    </item>
+    <item>
+     <widget class="QgsMapLayerComboBox" name="mMapLayerComboBox"/>
+    </item>
     <item>
      <widget class="QGroupBox" name="groupBoxLabelProperties">
       <property name="enabled">
@@ -178,6 +188,13 @@
    </layout>
   </widget>
  </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QgsMapLayerComboBox</class>
+   <extends>QComboBox</extends>
+   <header>qgsmaplayercombobox.h</header>
+  </customwidget>
+ </customwidgets>
  <resources>
   <include location="resources.qrc"/>
  </resources>
diff --git a/timeseriesviewer/ui/mapviewdefinition.ui b/timeseriesviewer/ui/mapviewdefinition.ui
index 7accd6ef5cc4cb604eaabff5bf72340b23bca03e..937749676802a31b29272404ea1eda1fca6da715 100644
--- a/timeseriesviewer/ui/mapviewdefinition.ui
+++ b/timeseriesviewer/ui/mapviewdefinition.ui
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>453</width>
-    <height>123</height>
+    <height>159</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -127,6 +127,13 @@
         </property>
        </widget>
       </item>
+      <item>
+       <widget class="QToolButton" name="btnShowCrosshair">
+        <property name="text">
+         <string>...</string>
+        </property>
+       </widget>
+      </item>
       <item>
        <widget class="QToolButton" name="btnVectorOverlayVisibility">
         <property name="sizePolicy">
@@ -255,6 +262,21 @@
     <string>Toogle visibility of overlayed vector file</string>
    </property>
   </action>
+  <action name="actionShowCrosshair">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>false</bool>
+   </property>
+   <property name="icon">
+    <iconset resource="resources.qrc">
+     <normaloff>:/timeseriesviewer/icons/crosshair.png</normaloff>:/timeseriesviewer/icons/crosshair.png</iconset>
+   </property>
+   <property name="text">
+    <string>Show Crosshair</string>
+   </property>
+  </action>
  </widget>
  <resources>
   <include location="resources.qrc"/>
diff --git a/timeseriesviewer/ui/renderingdock.ui b/timeseriesviewer/ui/renderingdock.ui
index 3ac2d56e2e1a99a6cb16ce319b7b0b010dc9bcf1..300b78942a38a65e4f2f1a224a163984635f2558 100644
--- a/timeseriesviewer/ui/renderingdock.ui
+++ b/timeseriesviewer/ui/renderingdock.ui
@@ -6,8 +6,8 @@
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>266</width>
-    <height>368</height>
+    <width>281</width>
+    <height>256</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -63,7 +63,7 @@
       <property name="minimumSize">
        <size>
         <width>0</width>
-        <height>100</height>
+        <height>0</height>
        </size>
       </property>
       <property name="maximumSize">
@@ -73,25 +73,19 @@
        </size>
       </property>
       <property name="title">
-       <string>Map Size</string>
+       <string>Map</string>
       </property>
       <property name="checkable">
        <bool>false</bool>
       </property>
-      <layout class="QFormLayout" name="formLayout_6">
+      <layout class="QGridLayout" name="gridLayout">
        <property name="margin">
         <number>6</number>
        </property>
-       <item row="0" column="0">
-        <widget class="QLabel" name="label_7">
-         <property name="minimumSize">
-          <size>
-           <width>30</width>
-           <height>0</height>
-          </size>
-         </property>
+       <item row="1" column="0">
+        <widget class="QLabel" name="label_6">
          <property name="text">
-          <string>width</string>
+          <string>height</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
@@ -144,16 +138,13 @@
          </property>
         </widget>
        </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="label_6">
+       <item row="0" column="2">
+        <widget class="QCheckBox" name="checkBoxKeepSubsetAspectRatio">
          <property name="text">
-          <string>height</string>
-         </property>
-         <property name="alignment">
-          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+          <string>keep aspect ratio</string>
          </property>
-         <property name="buddy">
-          <cstring>spinBoxSubsetSizeY</cstring>
+         <property name="checked">
+          <bool>true</bool>
          </property>
         </widget>
        </item>
@@ -197,86 +188,59 @@
          </property>
         </widget>
        </item>
-       <item row="2" column="0" colspan="2">
-        <widget class="QCheckBox" name="checkBoxKeepSubsetAspectRatio">
-         <property name="text">
-          <string>keep aspect ratio</string>
-         </property>
-         <property name="checked">
-          <bool>true</bool>
-         </property>
-        </widget>
-       </item>
-      </layout>
-     </widget>
-    </item>
-    <item>
-     <widget class="QgsCollapsibleGroupBox" name="gbCrosshair">
-      <property name="title">
-       <string>Show Crosshair</string>
-      </property>
-      <property name="checkable">
-       <bool>true</bool>
-      </property>
-      <property name="checked">
-       <bool>false</bool>
-      </property>
-      <property name="saveCheckedState">
-       <bool>true</bool>
-      </property>
-      <layout class="QFormLayout" name="formLayout_4">
        <item row="0" column="0">
-        <widget class="QLabel" name="label_13">
-         <property name="enabled">
-          <bool>false</bool>
+        <widget class="QLabel" name="label_7">
+         <property name="minimumSize">
+          <size>
+           <width>30</width>
+           <height>0</height>
+          </size>
          </property>
          <property name="text">
-          <string>Type</string>
+          <string>width</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+         </property>
+         <property name="buddy">
+          <cstring>spinBoxSubsetSizeY</cstring>
          </property>
         </widget>
        </item>
-       <item row="0" column="1">
-        <widget class="QComboBox" name="cbCrosshairStyle">
-         <property name="enabled">
-          <bool>false</bool>
+       <item row="0" column="3">
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
          </property>
-         <item>
-          <property name="text">
-           <string>Cross</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Dot</string>
-          </property>
-         </item>
-         <item>
-          <property name="text">
-           <string>Cross + Dot</string>
-          </property>
-         </item>
-        </widget>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
        </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="label_14">
+       <item row="2" column="0">
+        <widget class="QLabel" name="label_15">
          <property name="enabled">
-          <bool>false</bool>
+          <bool>true</bool>
          </property>
          <property name="text">
           <string>Color</string>
          </property>
         </widget>
        </item>
-       <item row="1" column="1">
-        <widget class="QgsColorButton" name="btnCrosshairColor">
-         <property name="enabled">
-          <bool>false</bool>
-         </property>
-         <property name="maximumSize">
-          <size>
-           <width>50</width>
-           <height>16777215</height>
-          </size>
+       <item row="2" column="1">
+        <widget class="QgsColorButton" name="btnMapCanvasColor">
+         <property name="colorDialogTitle">
+          <string>Select Map Canvas Background Color</string>
+         </property>
+         <property name="color">
+          <color>
+           <red>0</red>
+           <green>0</green>
+           <blue>0</blue>
+          </color>
          </property>
         </widget>
        </item>
diff --git a/timeseriesviewer/ui/resources.qrc b/timeseriesviewer/ui/resources.qrc
index 0037313e1fe4c46d102111280c44fe7a994e6765..c500b553915b79b14d42d04364fccd7580fcc7bf 100644
--- a/timeseriesviewer/ui/resources.qrc
+++ b/timeseriesviewer/ui/resources.qrc
@@ -2,6 +2,7 @@
   <qresource prefix="timeseriesviewer">
     <file>icons/ActionIdentifyTimeSeries.png</file>
     <file>icons/CRS.png</file>
+    <file>icons/crosshair.png</file>
     <file>icons/IconTimeSeries.png</file>
     <file>icons/copyright_label.png</file>
     <file>icons/general.png</file>
diff --git a/timeseriesviewer/ui/timeseriesviewer.ui b/timeseriesviewer/ui/timeseriesviewer.ui
index 820ab4dd8c7c92cafe4b6d5122eef200691bdca2..b90cfcb292d7cca9836223d92cecf1c98b6be824 100644
--- a/timeseriesviewer/ui/timeseriesviewer.ui
+++ b/timeseriesviewer/ui/timeseriesviewer.ui
@@ -162,6 +162,7 @@
     <addaction name="actionRefresh"/>
     <addaction name="actionAddMapView"/>
     <addaction name="menuPanels"/>
+    <addaction name="actionShowCrosshair"/>
    </widget>
    <widget class="QMenu" name="menuNavigation">
     <property name="title">
@@ -546,6 +547,21 @@
     <string>F5</string>
    </property>
   </action>
+  <action name="actionShowCrosshair">
+   <property name="checkable">
+    <bool>true</bool>
+   </property>
+   <property name="checked">
+    <bool>false</bool>
+   </property>
+   <property name="icon">
+    <iconset resource="resources.qrc">
+     <normaloff>:/timeseriesviewer/icons/crosshair.png</normaloff>:/timeseriesviewer/icons/crosshair.png</iconset>
+   </property>
+   <property name="text">
+    <string>Show Crosshair</string>
+   </property>
+  </action>
  </widget>
  <customwidgets>
   <customwidget>
diff --git a/timeseriesviewer/ui/tmp.ui b/timeseriesviewer/ui/tmp.ui
new file mode 100644
index 0000000000000000000000000000000000000000..8bcb13acdb3d06714c847d79045323535c82d6ae
--- /dev/null
+++ b/timeseriesviewer/ui/tmp.ui
@@ -0,0 +1,993 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<ui version="4.0">
+ <class>MapViewRenderSettings</class>
+ <widget class="QFrame" name="MapViewRenderSettings">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>260</width>
+    <height>135</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy vsizetype="Preferred" hsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>260</width>
+    <height>135</height>
+   </size>
+  </property>
+  <property name="maximumSize">
+   <size>
+    <width>260</width>
+    <height>151</height>
+   </size>
+  </property>
+  <property name="font">
+   <font>
+    <pointsize>8</pointsize>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>RenderSettings</string>
+  </property>
+  <property name="title" stdset="0">
+   <string/>
+  </property>
+  <property name="flat" stdset="0">
+   <bool>false</bool>
+  </property>
+  <property name="checkable" stdset="0">
+   <bool>false</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>2</number>
+   </property>
+   <property name="leftMargin">
+    <number>2</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>2</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2">
+     <item>
+      <widget class="QLabel" name="labelTitle">
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+         <weight>50</weight>
+         <italic>false</italic>
+         <bold>false</bold>
+         <kerning>true</kerning>
+        </font>
+       </property>
+       <property name="toolTip">
+        <string>Sensor name</string>
+       </property>
+       <property name="text">
+        <string>Sensor Name</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="cbRenderType">
+       <property name="maximumSize">
+        <size>
+         <width>60</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <item>
+        <property name="text">
+         <string>Multi</string>
+        </property>
+       </item>
+       <item>
+        <property name="text">
+         <string>Single</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="labelSummary">
+       <property name="sizePolicy">
+        <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>0</width>
+         <height>15</height>
+        </size>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>&lt;RGB Bands></string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <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>
+   <item>
+    <widget class="QStackedWidget" name="stackedWidget">
+     <property name="minimumSize">
+      <size>
+       <width>250</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="frameShape">
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="currentIndex">
+      <number>1</number>
+     </property>
+     <widget class="QWidget" name="pageMultiBand">
+      <layout class="QGridLayout" name="gridLayout_2">
+       <property name="leftMargin">
+        <number>2</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>2</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <property name="horizontalSpacing">
+        <number>2</number>
+       </property>
+       <property name="verticalSpacing">
+        <number>0</number>
+       </property>
+       <item row="1" column="1">
+        <widget class="QSlider" name="sliderRed">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>1</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="styleSheet">
+          <string notr="true"/>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>6</number>
+         </property>
+         <property name="value">
+          <number>3</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="tickPosition">
+          <enum>QSlider::TicksAbove</enum>
+         </property>
+         <property name="tickInterval">
+          <number>1</number>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="2">
+        <widget class="QLabel" name="labelMax">
+         <property name="text">
+          <string>Min</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <widget class="QLineEdit" name="tbRedMin">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>0</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="2">
+        <widget class="QLineEdit" name="tbGreenMin">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>0</string>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="3">
+        <widget class="QLineEdit" name="tbRedMax">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>5000</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="3">
+        <widget class="QLabel" name="labelMin">
+         <property name="text">
+          <string>Max</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="3">
+        <widget class="QLineEdit" name="tbGreenMax">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>5000</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="0">
+        <widget class="QLabel" name="labelGreen">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="styleSheet">
+          <string notr="true"/>
+         </property>
+         <property name="text">
+          <string>G</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QSlider" name="sliderGreen">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="styleSheet">
+          <string notr="true"/>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>6</number>
+         </property>
+         <property name="value">
+          <number>2</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="tickPosition">
+          <enum>QSlider::TicksAbove</enum>
+         </property>
+         <property name="tickInterval">
+          <number>1</number>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="0">
+        <widget class="QLabel" name="labelRed">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="styleSheet">
+          <string notr="true"/>
+         </property>
+         <property name="text">
+          <string>R</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="0">
+        <widget class="QLabel" name="labelBlue">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>B</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="1">
+        <widget class="QSlider" name="sliderBlue">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>6</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="tickPosition">
+          <enum>QSlider::TicksAbove</enum>
+         </property>
+         <property name="tickInterval">
+          <number>1</number>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="2">
+        <widget class="QLineEdit" name="tbBlueMin">
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>0</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="3">
+        <widget class="QLineEdit" name="tbBlueMax">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>5000</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="0" colspan="2">
+        <widget class="QFrame" name="btnBarMB">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Preferred" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="margin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QToolButton" name="btnDefaultMB">
+            <property name="font">
+             <font>
+              <pointsize>8</pointsize>
+              <italic>false</italic>
+             </font>
+            </property>
+            <property name="toolTip">
+             <string>default band selection</string>
+            </property>
+            <property name="text">
+             <string>D</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnTrueColor">
+            <property name="font">
+             <font>
+              <pointsize>8</pointsize>
+              <italic>false</italic>
+             </font>
+            </property>
+            <property name="toolTip">
+             <string>red-green-blue (true colour)</string>
+            </property>
+            <property name="text">
+             <string>TC</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnCIR">
+            <property name="font">
+             <font>
+              <pointsize>8</pointsize>
+              <italic>false</italic>
+             </font>
+            </property>
+            <property name="toolTip">
+             <string>swIR-red-green (coloured infra-red)</string>
+            </property>
+            <property name="text">
+             <string>CIR</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btn453">
+            <property name="font">
+             <font>
+              <pointsize>8</pointsize>
+              <italic>false</italic>
+             </font>
+            </property>
+            <property name="toolTip">
+             <string>swIR-mwIR-red</string>
+            </property>
+            <property name="text">
+             <string>453</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <spacer name="horizontalSpacer">
+            <property name="orientation">
+             <enum>Qt::Horizontal</enum>
+            </property>
+            <property name="sizeHint" stdset="0">
+             <size>
+              <width>0</width>
+              <height>0</height>
+             </size>
+            </property>
+           </spacer>
+          </item>
+         </layout>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="pageSingleBand">
+      <layout class="QGridLayout" name="gridLayout">
+       <property name="horizontalSpacing">
+        <number>2</number>
+       </property>
+       <property name="verticalSpacing">
+        <number>0</number>
+       </property>
+       <property name="margin">
+        <number>0</number>
+       </property>
+       <item row="3" column="1">
+        <widget class="QSlider" name="sliderSingleBand">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="maximumSize">
+          <size>
+           <width>16777215</width>
+           <height>16777215</height>
+          </size>
+         </property>
+         <property name="minimum">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <number>6</number>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="tickPosition">
+          <enum>QSlider::TicksAbove</enum>
+         </property>
+         <property name="tickInterval">
+          <number>1</number>
+         </property>
+        </widget>
+       </item>
+       <item row="8" column="1" colspan="4">
+        <widget class="QFrame" name="frame">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="frameShape">
+          <enum>QFrame::StyledPanel</enum>
+         </property>
+         <property name="frameShadow">
+          <enum>QFrame::Raised</enum>
+         </property>
+         <layout class="QGridLayout" name="gridLayout_4">
+          <property name="horizontalSpacing">
+           <number>1</number>
+          </property>
+          <property name="verticalSpacing">
+           <number>0</number>
+          </property>
+          <property name="margin">
+           <number>0</number>
+          </property>
+          <item row="1" column="2">
+           <widget class="QComboBox" name="cbSingleBandColorRampType">
+            <property name="sizePolicy">
+             <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+              <horstretch>2</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="toolTip">
+             <string>Type of color ramp</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="0">
+           <widget class="QgsColorRampComboBox" name="cbSingleBandColorRamp">
+            <property name="sizePolicy">
+             <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+              <horstretch>1</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="maximumSize">
+             <size>
+              <width>16777215</width>
+              <height>16777215</height>
+             </size>
+            </property>
+            <property name="toolTip">
+             <string>Color ramp</string>
+            </property>
+           </widget>
+          </item>
+          <item row="1" column="3">
+           <widget class="QComboBox" name="cbSingleBandMode">
+            <property name="sizePolicy">
+             <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+              <horstretch>2</horstretch>
+              <verstretch>0</verstretch>
+             </sizepolicy>
+            </property>
+            <property name="toolTip">
+             <string>Color classification</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QFrame" name="bntBarSB">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>0</height>
+          </size>
+         </property>
+         <layout class="QHBoxLayout" name="horizontalLayout_3">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="margin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandDef">
+            <property name="font">
+             <font>
+              <italic>false</italic>
+             </font>
+            </property>
+            <property name="text">
+             <string>D</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandBlue">
+            <property name="toolTip">
+             <string>Select band from visible blue</string>
+            </property>
+            <property name="text">
+             <string>B</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandGreen">
+            <property name="toolTip">
+             <string>Select band from visible green</string>
+            </property>
+            <property name="text">
+             <string>G</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandRed">
+            <property name="toolTip">
+             <string>Select band from visible red</string>
+            </property>
+            <property name="text">
+             <string>R</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandNIR">
+            <property name="toolTip">
+             <string>Select band from near infra-red</string>
+            </property>
+            <property name="text">
+             <string>nIR</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QToolButton" name="btnSingleBandSWIR">
+            <property name="toolTip">
+             <string>Select band from shortwave infra-red</string>
+            </property>
+            <property name="text">
+             <string>swIR</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="2" column="3">
+        <widget class="QLabel" name="labelMin_2">
+         <property name="text">
+          <string>Min</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="3">
+        <widget class="QLineEdit" name="tbSingleBandMin">
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>0</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="4">
+        <widget class="QLabel" name="labelMax_2">
+         <property name="text">
+          <string>Max</string>
+         </property>
+        </widget>
+       </item>
+       <item row="3" column="4">
+        <widget class="QLineEdit" name="tbSingleBandMax">
+         <property name="sizePolicy">
+          <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+           <horstretch>0</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>50</width>
+           <height>0</height>
+          </size>
+         </property>
+         <property name="font">
+          <font>
+           <pointsize>8</pointsize>
+          </font>
+         </property>
+         <property name="text">
+          <string>5000</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <item>
+      <widget class="QToolButton" name="btnApplyStyle">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="icon">
+        <iconset resource="resources.qrc">
+         <normaloff>:/timeseriesviewer/icons/mActionRefresh.png</normaloff>:/timeseriesviewer/icons/mActionRefresh.png</iconset>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnCopyStyle">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="icon">
+        <iconset resource="resources.qrc">
+         <normaloff>:/timeseriesviewer/icons/mActionEditCopy.png</normaloff>:/timeseriesviewer/icons/mActionEditCopy.png</iconset>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="btnPasteStyle">
+       <property name="text">
+        <string>...</string>
+       </property>
+       <property name="icon">
+        <iconset resource="resources.qrc">
+         <normaloff>:/timeseriesviewer/icons/mActionEditPaste.png</normaloff>:/timeseriesviewer/icons/mActionEditPaste.png</iconset>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+        </font>
+       </property>
+       <property name="text">
+        <string>Stretch</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QComboBox" name="comboBoxContrastEnhancement">
+       <property name="sizePolicy">
+        <sizepolicy vsizetype="Fixed" hsizetype="Preferred">
+         <horstretch>1</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <pointsize>8</pointsize>
+        </font>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+  <action name="actionSetDefaultMB">
+   <property name="text">
+    <string>Def</string>
+   </property>
+   <property name="toolTip">
+    <string>Set default band selection</string>
+   </property>
+  </action>
+  <action name="actionSetTrueColor">
+   <property name="text">
+    <string>True</string>
+   </property>
+   <property name="toolTip">
+    <string>Set to true color (red-green-blue)</string>
+   </property>
+  </action>
+  <action name="actionSetCIR">
+   <property name="text">
+    <string>CIR</string>
+   </property>
+   <property name="toolTip">
+    <string>Set to coloured infra red (swIR-red-green)</string>
+   </property>
+  </action>
+  <action name="actionSet453">
+   <property name="text">
+    <string>453</string>
+   </property>
+   <property name="toolTip">
+    <string>Set to swIR-mwIR-red (Landsat 4-5-3)</string>
+   </property>
+  </action>
+  <action name="actionCopyStyle">
+   <property name="icon">
+    <iconset resource="resources.qrc">
+     <normaloff>:/timeseriesviewer/icons/mActionEditCopy.png</normaloff>:/timeseriesviewer/icons/mActionEditCopy.png</iconset>
+   </property>
+   <property name="text">
+    <string>Copy style</string>
+   </property>
+   <property name="toolTip">
+    <string>Copy style to clipboard</string>
+   </property>
+  </action>
+  <action name="actionPasteStyle">
+   <property name="icon">
+    <iconset resource="resources.qrc">
+     <normaloff>:/timeseriesviewer/icons/mActionEditPaste.png</normaloff>:/timeseriesviewer/icons/mActionEditPaste.png</iconset>
+   </property>
+   <property name="text">
+    <string>Paste Style</string>
+   </property>
+   <property name="toolTip">
+    <string>Paste style from clipboard</string>
+   </property>
+  </action>
+  <action name="actionApplyStyle">
+   <property name="icon">
+    <iconset resource="resources.qrc">
+     <normaloff>:/timeseriesviewer/icons/mActionRefresh.png</normaloff>:/timeseriesviewer/icons/mActionRefresh.png</iconset>
+   </property>
+   <property name="text">
+    <string>ApplySettings</string>
+   </property>
+   <property name="toolTip">
+    <string>Apply Style</string>
+   </property>
+  </action>
+  <action name="actionSetR">
+   <property name="text">
+    <string>R</string>
+   </property>
+  </action>
+  <action name="actionSetG">
+   <property name="text">
+    <string>G</string>
+   </property>
+  </action>
+  <action name="actionSetB">
+   <property name="text">
+    <string>B</string>
+   </property>
+  </action>
+  <action name="actionSetNIR">
+   <property name="text">
+    <string>nIR</string>
+   </property>
+  </action>
+  <action name="actionSetSWIR">
+   <property name="text">
+    <string>swIR</string>
+   </property>
+  </action>
+  <action name="actionSetDefaultSB">
+   <property name="text">
+    <string>Def</string>
+   </property>
+  </action>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>QgsColorRampComboBox</class>
+   <extends>QComboBox</extends>
+   <header>qgis.gui</header>
+  </customwidget>
+ </customwidgets>
+ <resources>
+  <include location="resources.qrc"/>
+ </resources>
+ <connections>
+  <connection>
+   <sender>cbRenderType</sender>
+   <signal>currentIndexChanged(int)</signal>
+   <receiver>stackedWidget</receiver>
+   <slot>setCurrentIndex(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>107</x>
+     <y>19</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>144</x>
+     <y>54</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
diff --git a/timeseriesviewer/ui/widgets.py b/timeseriesviewer/ui/widgets.py
index c9d42b5b190f39ee90e8eeb101e86f51b25ac54d..5925b150d184c166845f52e25e10dde42c08cb71 100644
--- a/timeseriesviewer/ui/widgets.py
+++ b/timeseriesviewer/ui/widgets.py
@@ -42,262 +42,6 @@ class TsvScrollArea(QScrollArea):
         self.sigResized.emit()
 
 
-class TsvMapCanvas(QgsMapCanvas):
-    from timeseriesviewer.main import SpatialExtent
-    saveFileDirectories = dict()
-    sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
-    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
-
-    def __init__(self, tsdView, mapView, parent=None):
-        super(TsvMapCanvas, self).__init__(parent=parent)
-        from timeseriesviewer.main import TimeSeriesDatumView, MapView
-        assert isinstance(tsdView, TimeSeriesDatumView)
-        assert isinstance(mapView, MapView)
-
-        #the canvas
-        self.setCrsTransformEnabled(True)
-        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
-        self.setCanvasColor(SETTINGS.value('CANVAS_BACKGROUND_COLOR', QColor(0, 0, 0)))
-        self.setContextMenuPolicy(Qt.DefaultContextMenu)
-
-        self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
-
-        self.scrollArea = tsdView.scrollArea
-        assert isinstance(self.scrollArea, TsvScrollArea)
-        self.scrollArea.sigResized.connect(self.setRenderMe)
-        self.scrollArea.horizontalScrollBar().valueChanged.connect(self.setRenderMe)
-
-        self.tsdView = tsdView
-        self.mapView = mapView
-        self.vectorLayer = None
-        self.mapView.sigVectorLayerChanged.connect(self.refresh)
-        self.mapView.sigVectorVisibility.connect(self.refresh)
-        self.renderMe = False
-        self.setRenderMe()
-
-        self.sensorView = self.mapView.sensorViews[self.tsdView.Sensor]
-        self.mapView.sigMapViewVisibility.connect(self.refresh)
-        self.mapView.sigSpatialExtentChanged.connect(self.setSpatialExtent)
-        self.referenceLayer = QgsRasterLayer(self.tsdView.TSD.pathImg)
-        QgsMapLayerRegistry.instance().addMapLayer(self.referenceLayer, False)
-
-        self.MapCanvasLayers = []
-        self.setMapLayers()
-
-        self.sensorView.sigSensorRendererChanged.connect(self.setRenderer)
-        self.setRenderer(self.sensorView.layerRenderer())
-
-
-        self.MAPTOOLS = dict()
-        self.MAPTOOLS['zoomOut'] = QgsMapToolZoom(self, True)
-        self.MAPTOOLS['zoomIn'] = QgsMapToolZoom(self, False)
-        self.MAPTOOLS['pan'] = QgsMapToolPan(self)
-        from timeseriesviewer.maptools import PointMapTool, PointLayersMapTool
-        mt = PointMapTool(self)
-        mt.sigCoordinateSelected.connect(self.sigShowProfiles.emit)
-        self.MAPTOOLS['identifyProfile'] = mt
-        #todo: self.MAPTOOLS['identifyMapLayers'] =
-
-    def setMapLayers(self, *args):
-        del self.MapCanvasLayers[:]
-        if self.mapView.visibleVectorOverlay():
-            #if necessary, register new vector layer
-            refLyr = self.mapView.vectorLayer
-            uri = refLyr.dataProvider().dataSourceUri()
-
-            if self.vectorLayer is None or self.vectorLayer.dataProvider().dataSourceUri() != uri:
-                providerKey = refLyr.dataProvider().name()
-                baseName = os.path.basename(uri)
-                self.vectorLayer = QgsVectorLayer(uri, baseName, providerKey)
-                QgsMapLayerRegistry.instance().addMapLayer(self.vectorLayer, False)
-
-            #update layer style
-            self.vectorLayer.setRendererV2(refLyr.rendererV2().clone())
-
-            self.MapCanvasLayers.append(QgsMapCanvasLayer(self.vectorLayer))
-        if self.referenceLayer:
-            self.MapCanvasLayers.append(QgsMapCanvasLayer(self.referenceLayer))
-
-        self.setLayerSet(self.MapCanvasLayers)
-
-
-
-    def refresh(self):
-        self.setMapLayers()
-        self.setRenderMe()
-        super(TsvMapCanvas, self).refresh()
-
-
-    def setRenderMe(self):
-        oldFlag = self.renderFlag()
-
-        newFlag = self.visibleRegion().boundingRect().isValid() and self.isVisible() and self.tsdView.TSD.isVisible()
-        if oldFlag != newFlag:
-            self.setRenderFlag(newFlag)
-        #print((self.tsdView.TSD, self.renderFlag()))
-        #return b.isValid()
-
-    def pixmap(self):
-        """
-        Returns the current map image as pixmap
-        :return:
-        """
-        return QPixmap(self.map().contentImage().copy())
-
-    def contextMenuEvent(self, event):
-        menu = QMenu()
-        # add general options
-        menu.addSeparator()
-        action = menu.addAction('Stretch using current Extent')
-        action.triggered.connect(self.stretchToCurrentExtent)
-        action = menu.addAction('Zoom to Layer')
-        action.triggered.connect(lambda : self.setExtent(SpatialExtent(self.referenceLayer.crs(),self.referenceLayer.extent())))
-        menu.addSeparator()
-
-
-        action = menu.addAction('Copy to Clipboard')
-        action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
-        m = menu.addMenu('Copy...')
-        action = menu.addAction('image path')
-        #action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.tsdView.TSD.pathImg))
-        action = menu.addAction('style')
-        #action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.tsdView.TSD.pathImg))
-
-        m = menu.addMenu('Save as...')
-        action = m.addAction('PNG')
-        action.triggered.connect(lambda : self.saveMapImageDialog('PNG'))
-        action = m.addAction('JPEG')
-        action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))
-
-        from timeseriesviewer.main import QgisTsvBridge
-        bridge = QgisTsvBridge.instance()
-        if bridge:
-            assert isinstance(bridge, QgisTsvBridge)
-            action = m.addAction('Add layer to QGIS')
-            action = m.addAction('Import extent from QGIS')
-            action = m.addAction('Export extent to QGIS')
-            s = ""
-
-
-
-
-        menu.addSeparator()
-        TSD = self.tsdView.TSD
-        action = menu.addAction('Hide date')
-        action.triggered.connect(lambda : self.tsdView.TSD.setVisibility(False))
-        action = menu.addAction('Remove date')
-        action.triggered.connect(lambda: TSD.timeSeries.removeDates([TSD]))
-        action = menu.addAction('Remove map view')
-        action.triggered.connect(lambda: self.mapView.sigRemoveMapView.emit(self.mapView))
-        action = menu.addAction('Hide map view')
-        action.triggered.connect(lambda: self.mapView.sigHideMapView.emit())
-
-
-        menu.exec_(event.globalPos())
-
-    def stretchToCurrentExtent(self):
-        results = dict()
-        se = self.spatialExtent()
-
-        for l in self.layers():
-            if isinstance(l, QgsRasterLayer):
-                r = l.renderer()
-                dp = l.dataProvider()
-                newRenderer = None
-
-                extent = se.toCrs(l.crs())
-
-                assert isinstance(dp, QgsRasterDataProvider)
-                bands = None
-                if isinstance(r, QgsMultiBandColorRenderer):
-
-                    def getCE(band, ce):
-                        stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 500)
-                        ce = QgsContrastEnhancement(ce)
-                        ce.setMinimumValue(stats.minimumValue)
-                        ce.setMaximumValue(stats.maximumValue)
-                        return ce
-
-                    newRenderer = QgsMultiBandColorRenderer(None,r.redBand(), r.greenBand(), r.blueBand())
-                    newRenderer.setRedContrastEnhancement(getCE(r.redBand(), r.redContrastEnhancement()))
-                    newRenderer.setGreenContrastEnhancement(getCE(r.greenBand(), r.greenContrastEnhancement()))
-                    newRenderer.setBlueContrastEnhancement(getCE(r.blueBand(), r.blueContrastEnhancement()))
-
-                    results[self.tsdView.TSD.sensor] = newRenderer
-                elif isinstance(r, QgsSingleBandPseudoColorRenderer):
-                    newRenderer = r.clone()
-                    stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)
-                    shader = newRenderer.shader()
-                    newRenderer.setClassificationMax(stats.maximumValue)
-                    newRenderer.setClassificationMin(stats.minimumValue)
-                    shader.setMaximumValue(stats.maximumValue)
-                    shader.setMinimumValue(stats.minimumValue)
-                    s = ""
-
-                if newRenderer is not None:
-                    self.sensorView.setLayerRenderer(newRenderer)
-        s = ""
-
-    def activateMapTool(self, key):
-        if key is None:
-            self.setMapTool(None)
-        elif key in self.MAPTOOLS.keys():
-            self.setMapTool(self.MAPTOOLS[key])
-        else:
-            from timeseriesviewer import dprint
-            dprint('unknown map tool key "{}"'.format(key))
-
-    def saveMapImageDialog(self, fileType):
-        lastDir = SETTINGS.value('CANVAS_SAVE_IMG_DIR', os.path.expanduser('~'))
-        path = jp(lastDir, '{}.{}.{}'.format(self.tsdView.TSD.date, self.mapView.title(), fileType.lower()))
-
-        path = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
-        if len(path) > 0:
-            self.saveAsImage(path, None, fileType)
-            SETTINGS.setValue('CANVAS_SAVE_IMG_DIR', os.path.dirname(path))
-
-
-    def setRenderer(self, renderer, targetLayerUri=None):
-        if targetLayerUri is None:
-            targetLayerUri = str(self.referenceLayer.source())
-
-        lyrs = [mcl.layer() for mcl in self.MapCanvasLayers if str(mcl.layer().source()) == targetLayerUri]
-        assert len(lyrs) <= 1
-        for lyr in lyrs:
-            if isinstance(renderer, QgsMultiBandColorRenderer):
-                r = renderer.clone()
-                r.setInput(lyr.dataProvider())
-            elif isinstance(renderer, QgsSingleBandPseudoColorRenderer):
-                r = renderer.clone()
-                #r = QgsSingleBandPseudoColorRenderer(None, renderer.band(), None)
-                r.setInput(lyr.dataProvider())
-                cmin = renderer.classificationMin()
-                cmax = renderer.classificationMax()
-                r.setClassificationMin(cmin)
-                r.setClassificationMax(cmax)
-                #r.setShader(renderer.shader())
-                s = ""
-            else:
-                raise NotImplementedError()
-            lyr.setRenderer(r)
-
-        self.refresh()
-
-    def setSpatialExtent(self, spatialExtent):
-        assert isinstance(spatialExtent, SpatialExtent)
-        if self.spatialExtent() != spatialExtent:
-            self.blockSignals(True)
-            self.setDestinationCrs(spatialExtent.crs())
-            self.setExtent(spatialExtent)
-            self.blockSignals(False)
-            self.refresh()
-
-
-    def spatialExtent(self):
-        return SpatialExtent.fromMapCanvas(self)
-
-
-
 class VerticalLabel(QLabel):
     def __init__(self, text, orientation='vertical', forceWidth=True):
         QLabel.__init__(self, text)
@@ -621,6 +365,9 @@ class MapViewDefinitionUI(QGroupBox, loadUIFormClass(PATH_MAPVIEWDEFINITION_UI))
         self.btnMapViewVisibility.setDefaultAction(self.actionToggleVisibility)
         self.btnApplyStyles.setDefaultAction(self.actionApplyStyles)
         self.btnVectorOverlayVisibility.setDefaultAction(self.actionToggleVectorVisibility)
+        self.btnShowCrosshair.setDefaultAction(self.actionShowCrosshair)
+
+
         self.actionToggleVisibility.toggled.connect(lambda: self.setVisibility(not self.actionToggleVisibility.isChecked()))
         self.actionToggleVectorVisibility.toggled.connect(lambda : self.sigVectorVisibility.emit(self.actionToggleVectorVisibility.isChecked()))
 
@@ -919,7 +666,8 @@ class MapViewSensorSettings(QObject):
         rgb = self.rgb()
 
         text = 'RGB {}-{}-{}'.format(*rgb)
-        if self.sensor.wavelengthsDefined():
+
+        if False and self.sensor.wavelengthsDefined():
             text += ' ({} {})'.format(
                 ','.join(['{:0.2f}'.format(self.sensor.wavelengths[b-1]) for b in rgb]),
                 self.sensor.wavelengthUnits)
diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py
index fd0fca056eeb6ae11003ac5e4b329680909b3b1d..d8f4d7d3e158c2b03620066ea3063f7564dbe625 100644
--- a/timeseriesviewer/utils.py
+++ b/timeseriesviewer/utils.py
@@ -1,7 +1,195 @@
 
 
 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 upperRightPt(self):
+        return QgsPoint(*self.upperRight())
+
+    def upperLeftPt(self):
+        return QgsPoint(*self.upperLeft())
+
+    def lowerRightPt(self):
+        return QgsPoint(*self.lowerRight())
+
+    def lowerLeftPt(self):
+        return QgsPoint(*self.lowerLeft())
+
+
+    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 +200,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
diff --git a/timeseriesviewerplugin.py b/timeseriesviewerplugin.py
index e6336744a33e63d730fd71dfbe5ab5c4ace7e4ec..e2369f21f4f5bf18c07bbe5d68fd6edbf53a8a55 100644
--- a/timeseriesviewerplugin.py
+++ b/timeseriesviewerplugin.py
@@ -1,4 +1,3 @@
-from __future__ import absolute_import
 import inspect
 import os
 import six
@@ -7,7 +6,8 @@ import sys
 import importlib
 import re
 import site
-
+import logging
+logger = logging.getLogger(__name__)
 from qgis.gui import *
 from qgis.core import *
 from PyQt4.QtCore import *
@@ -40,7 +40,7 @@ class TimeSeriesViewerPlugin:
 
         import timeseriesviewer
         # init main UI
-        from timeseriesviewer import dprint, DIR_UI, jp
+        from timeseriesviewer import DIR_UI, jp
         icon = QIcon(jp(DIR_UI, *['icons', 'icon.png']))
         action = QAction(icon, 'HUB Time Series Viewer', self.iface)
         action.triggered.connect(self.run)
@@ -60,9 +60,8 @@ class TimeSeriesViewerPlugin:
         self.tsv.run()
 
     def unload(self):
-        from timeseriesviewer import dprint
         from timeseriesviewer.main import TimeSeriesViewer
-        dprint('UNLOAD TimeSeriesViewer Plugin')
+
 
         for action in self.toolbarActions:
             print(action)