diff --git a/CHANGES.txt b/CHANGES.txt
index cfc33cc12086112e1c77b3c49631b9a79f2f1242..cd8b82e757d87aeaabcfdef1da2a9a62874944b1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,9 @@
-2918-06-20
+2018-11-09
+        - uses QgsTaskManager for background loading
+        - own QgsMapLayerStore to not mix-up with (main) QGIS layers
+        - fixed bugs related to changes in QGIS API
+
+2018-06-20
         - increase version to 0.7
         - Visualization of images with stacked temporal information (each band = one observation date)
         - some bugfixes
diff --git a/test/test_pixelloader.py b/test/test_pixelloader.py
index 99c9513065b0e1d55a44840f9334863d82fd6ea9..24f2090e183d980afa8320637f6c1037a531c161 100644
--- a/test/test_pixelloader.py
+++ b/test/test_pixelloader.py
@@ -14,18 +14,30 @@ __author__ = 'benjamin.jakimow@geo.hu-berlin.de'
 
 import unittest
 import os, sys, pickle
-
+import qgis.testing
 from timeseriesviewer.utils import initQgisApplication
 import example.Images
-from timeseriesviewer.utils import file_search
-
-QGIS_APP = initQgisApplication()
+from timeseriesviewer.utils import *
 
+QGIS_APP = qgis.testing.start_app(False)
+#QGIS_APP = initQgisApplication()
+SHOW_GUI = True
 
 def onDummy(*args):
     print(('dummy', args))
 
 
+def waitForEnd(pl:PixelLoader):
+    print('wait for end...')
+
+    while QgsApplication.taskManager().countActiveTasks() > 0:
+        QCoreApplication.processEvents()
+
+
+    QCoreApplication.processEvents()
+    print('done')
+
+
 class PixelLoaderTest(unittest.TestCase):
     """Test translations work."""
 
@@ -34,7 +46,7 @@ class PixelLoaderTest(unittest.TestCase):
         from timeseriesviewer import DIR_EXAMPLES
         from timeseriesviewer.utils import file_search
         cls.imgs = file_search(DIR_EXAMPLES, '*.tif', recursive=True)
-        cls.img1 = cls.imgs[0]
+        cls.img1 = list(cls.imgs)[0]
         ds = gdal.Open(cls.img1)
         assert isinstance(ds, gdal.Dataset)
         nb, nl, ns = ds.RasterCount, ds.RasterYSize, ds.RasterXSize
@@ -55,8 +67,79 @@ class PixelLoaderTest(unittest.TestCase):
         """Runs after each test."""
         pass
 
+    def createTestImageSeries(self, n=10)->list:
+        return TestObjects.createTestImageSeries(n=n)
+
+    def test_QgsTaskManager(self):
+
+        nTasks = 0
+        result = None
+        def countTasks(*args):
+            nonlocal nTasks
+            nTasks += 1
+
+        tm = QgsApplication.taskManager()
+        self.assertIsInstance(tm, QgsTaskManager)
+        tm.taskAdded.connect(countTasks)
+
+        def func1(taskWrapper:QgsTaskWrapper):
+            return 'Hello'
+
+        def onFinished(e, value):
+            nonlocal result
+            assert e is None
+            result = value
+
+
+        task = QgsTask.fromFunction('',func1, on_finished = onFinished)
+        self.assertIsInstance(task, QgsTask)
+        tm.addTask(task)
+
+        while task.status() not in [QgsTask.Complete, QgsTask.Terminated]:
+            pass
+        while QgsApplication.taskManager().countActiveTasks() > 0:
+            QCoreApplication.processEvents()
+        self.assertTrue(nTasks == 1)
+        self.assertTrue(result == 'Hello')
+
+    def test_PixelLoader(self):
+
+        images = self.createTestImageSeries(n=50)
+
+        RESULTS = []
+        def onPixelLoaded(taskResult, *args):
+            nonlocal RESULTS
+            print('Pixel loaded: {}'.format(taskResult))
+            RESULTS.append(taskResult)
+
+        paths = [i.GetFileList()[0] for i in images]
+        rl = QgsRasterLayer(paths[0])
 
+        self.assertIsInstance(rl, QgsRasterLayer)
+        self.assertTrue(rl.isValid())
 
+        center = SpatialPoint.fromMapLayerCenter(rl)
+        cleft = SpatialPoint(center.crs(), center.x()-100, center.y())
+
+        geometries = [center, cleft]
+
+        pl = PixelLoader()
+        pl.sigPixelLoaded.connect(onPixelLoaded)
+        self.assertIsInstance(pl, PixelLoader)
+
+        tasks = []
+        for p in paths:
+            task = PixelLoaderTask(p, geometries)
+            tasks.append(task)
+        pl.startLoading(tasks)
+
+        waitForEnd(pl)
+        QCoreApplication.processEvents()
+
+        import time
+        time.sleep(5)
+
+        self.assertTrue(len(RESULTS) == len(tasks))
 
     def test_pixelLoader(self):
         from timeseriesviewer.pixelloader import doLoaderTask, PixelLoaderTask, INFO_OUT_OF_IMAGE, INFO_NO_DATA
@@ -79,9 +162,11 @@ class PixelLoaderTest(unittest.TestCase):
         ptValid2 = SpatialPoint(ext.crs(), x+50, y+50)
 
         #test a valid pixels
-
+        plt = PixelLoaderTask(source, [ptValid1, ptValid2])
+        task = QgsTask.fromFunction('', doLoaderTask, plt)
         try:
-            result = doLoaderTask(PixelLoaderTask(source, [ptValid1, ptValid2]))
+            result = doLoaderTask(task, plt.toDump())
+            result = PixelLoaderTask.fromDump(result)
         except Exception as ex:
             self.fail('Failed to return the pixels for two geometries')
 
@@ -111,21 +196,12 @@ class PixelLoaderTest(unittest.TestCase):
 
 
 
-        #test a out-of-image geometry
-        result = doLoaderTask(PixelLoaderTask(source, ptOutOfImage))
-        self.assertTrue(result.success())
-        self.assertEqual(result.resProfiles[0], INFO_OUT_OF_IMAGE)
-
-        result = doLoaderTask(PixelLoaderTask(source, ptNoData))
-        self.assertTrue(result.success())
-        self.assertEqual(result.resProfiles[0], INFO_NO_DATA)
 
     def test_loadProfiles(self):
 
         from timeseriesviewer.utils import SpatialPoint, SpatialExtent, px2geo
 
 
-
         img1 = self.img1
         nb, nl, ns = self.img1Shape
         gt = self.img1gt
@@ -155,25 +231,20 @@ class PixelLoaderTest(unittest.TestCase):
         geoms2 = [SpatialPoint(ext.crs(), x, y),
                   SpatialPoint(ext.crs(), x + 250, y + 70)]
 
-        from multiprocessing import Pool
-
+        loaded_values = []
         def onPxLoaded(*args):
-            n, nmax, task = args
-            assert isinstance(task, PixelLoaderTask)
-            print(task)
+            print('got loaded')
+            task = args[0]
+            nonlocal loaded_values
+            self.assertIsInstance(task, PixelLoaderTask)
+            loaded_values.append(task)
 
-        PL = PixelLoader()
 
 
-        def onTimer(*args):
-            print(('TIMER', PL))
-            pass
-
+        PL = PixelLoader()
         PL.sigPixelLoaded.connect(onPxLoaded)
         PL.sigLoadingFinished.connect(lambda: onDummy('finished'))
-        PL.sigLoadingCanceled.connect(lambda: onDummy('canceled'))
         PL.sigLoadingStarted.connect(lambda: onDummy('started'))
-        PL.sigPixelLoaded.connect(lambda: onDummy('px loaded'))
 
         tasks1 = []
         for i, f in enumerate(files):
@@ -185,18 +256,21 @@ class PixelLoaderTest(unittest.TestCase):
             kwargs = {'myid': 'myID{}'.format(i)}
             tasks2.append(PixelLoaderTask(f, geoms2, bandIndices=None, **kwargs))
 
-        for t in tasks1:
-            result = doLoaderTask(t)
-            s = ""
 
         PL.startLoading(tasks1)
         PL.startLoading(tasks2)
 
+        waitForEnd(PL)
+
+
+        self.assertTrue(len(loaded_values) == len(tasks2)+len(tasks1))
         print('DONE')
 
 
 
 
 if __name__ == "__main__":
+    SHOW_GUI = False
+
     unittest.main()
 
diff --git a/test/test_temporalprofiles.py b/test/test_temporalprofiles.py
index 5070ed598ea1ec668f8bee17e3dc8d524397a098..45f057d2ea2ec6ba62b9a1ffb874e8b0ae40c527 100644
--- a/test/test_temporalprofiles.py
+++ b/test/test_temporalprofiles.py
@@ -23,6 +23,7 @@ from timeseriesviewer.profilevisualization import *
 from timeseriesviewer.utils import *
 from osgeo import ogr, osr
 QGIS_APP = initQgisApplication()
+SHOW_GUI = True
 
 class testclassUtilityTests(unittest.TestCase):
     """Test rerources work."""
@@ -49,10 +50,12 @@ class testclassUtilityTests(unittest.TestCase):
 
         lyr = TemporalProfileLayer(self.TS)
 
-
-        tp1 = lyr.createTemporalProfiles(center)[0]
-        tp2 = lyr.createTemporalProfiles(SpatialPoint(center.crs(), center.x() + 40, center.y() + 50))
-        return [tp1, tp2]
+        results = []
+        results.extend(lyr.createTemporalProfiles(center))
+        results.extend(lyr.createTemporalProfiles(SpatialPoint(center.crs(), center.x() + 40, center.y() + 50)))
+        for p in results:
+            self.assertIsInstance(p, TemporalProfile)
+        return results
 
     def test_createTemporalProfile(self):
 
@@ -213,8 +216,8 @@ class testclassUtilityTests(unittest.TestCase):
         svis.loadCoordinate(point2)
         svis.ui.show()
 
-        s = ""
-        QGIS_APP.exec_()
+        if SHOW_GUI:
+            QGIS_APP.exec_()
 
 
 if __name__ == "__main__":
diff --git a/timeseriesviewer/main.py b/timeseriesviewer/main.py
index ca74817e882b5548a035c703af4b54ccb9188ad8..9f8cf02556718324408387af537de3a3aee43840 100644
--- a/timeseriesviewer/main.py
+++ b/timeseriesviewer/main.py
@@ -210,7 +210,7 @@ class TimeSeriesViewerUI(QMainWindow,
         self.addActions(self.findChildren(QAction))
         from timeseriesviewer import TITLE, icon, __version__
 
-        self.setWindowTitle('{} ()'.format(TITLE, __version__))
+        self.setWindowTitle('{} ({})'.format(TITLE, __version__))
         self.setWindowIcon(icon())
         if sys.platform == 'darwin':
             self.menuBar().setNativeMenuBar(False)
@@ -817,7 +817,7 @@ class TimeSeriesViewer(QgisInterface, QObject):
                     vectorLayers.append(l)
                 except Exception as ex:
                     pass
-            QgsProject.instance().addMapLayers(vectorLayers)
+            self.mapLayerStore().addMapLayers(vectorLayers)
 
 
     def addTimeSeriesImages(self, files=None):
diff --git a/timeseriesviewer/pixelloader.py b/timeseriesviewer/pixelloader.py
index dd828b011dba2f8ee5351423e9072bb076b84b98..dc6f755455dc548bd55bdc721fe60a362ad95a75 100644
--- a/timeseriesviewer/pixelloader.py
+++ b/timeseriesviewer/pixelloader.py
@@ -68,14 +68,6 @@ class PixelLoaderTask(object):
         return pickle.loads(byte_object)
 
     def __init__(self, source:str, geometries, bandIndices=None, **kwargs):
-        """
-
-        :param jobId: jobId number as given by the calling PixelLoader
-        :param processId: processId, as managed by the calling PixelLoader
-        :param geometry: SpatialPoint that describes the pixels to be loaded
-        :param source: file path to raster image.
-        :param kwargs: additional stuff returned, e.g. to identify somethin
-        """
 
         if not isinstance(geometries, list):
             geometries = [geometries]
@@ -84,6 +76,8 @@ class PixelLoaderTask(object):
             assert type(geometry) in [SpatialExtent, SpatialPoint]
 
 
+        self.mId = ''
+
         #assert isinstance(source, str) or isinstance(source, unicode)
         self.sourcePath = source
         self.geometries = geometries
@@ -107,6 +101,12 @@ class PixelLoaderTask(object):
             if not k in self.__dict__.keys():
                 self.__dict__[k] = kwargs[k]
 
+    def setId(self, idStr:str):
+        self.mId = idStr
+
+    def id(self)->str:
+        return self.mId
+
     def toDump(self):
         return pickle.dumps(self)
 
@@ -187,16 +187,21 @@ def transformPoint2Px(trans, pt, gt):
     x, y, _ = trans.TransformPoint(pt.x(), pt.y())
     return geo2px(QgsPointXY(x, y), gt)
 
-def doLoaderTask(task):
 
-    #assert isinstance(task, PixelLoaderTask), '{}\n{}'.format(type(task), task)
 
-    result = task
 
-    ds = gdal.Open(task.sourcePath, gdal.GA_ReadOnly)
-    nb, ns, nl = ds.RasterCount, ds.RasterXSize, ds.RasterYSize
 
+def doLoaderTask(taskWrapper:QgsTask, dump):
 
+    #assert isinstance(taskWrapper, QgsTask)
+    if isinstance(dump, PixelLoaderTask):
+        task = dump
+    else:
+        task = PixelLoaderTask.fromDump(dump)
+    assert isinstance(task, PixelLoaderTask)
+    result = task
+    ds = gdal.Open(task.sourcePath, gdal.GA_ReadOnly)
+    nb, ns, nl = ds.RasterCount, ds.RasterXSize, ds.RasterYSize
 
     bandIndices = list(range(nb)) if task.bandIndices is None else list(task.bandIndices)
     #ensure to load valid indices only
@@ -353,53 +358,7 @@ def doLoaderTask(task):
             s = ""
     task.resProfiles = PROFILE_DATA
     task.mIsDone = True
-    return task
-
-
-
-def pixelLoadingLoop(inputQueue, resultQueue, cancelEvent, finishedEvent):
-    import time
-    from multiprocessing.queues import Queue
-    from multiprocessing.synchronize import Event
-    assert isinstance(inputQueue, Queue)
-    assert isinstance(resultQueue, Queue)
-    assert isinstance(cancelEvent, Event)
-    assert isinstance(finishedEvent, Event)
-
-    dprint('Pixel Loading Loop Started')
-    #while not inputQueue.empty():
-    while True:
-        if cancelEvent.is_set():
-            dprint('Taskloop put CANCELED')
-            #resultQueue.put('CANCELED', True)
-            resultQueue.put('CANCELED')
-        #if not inputQueue.empty():
-
-        queueObj = inputQueue.get()
-        if isinstance(queueObj, bytes):
-            task = PixelLoaderTask.fromDump(queueObj)
-            try:
-                dprint('Taskloop {} doLoaderTask'.format(task.mJobId))
-                task = doLoaderTask(task)
-                dprint('Taskloop {} put task result back to queue'.format(task.mJobId))
-                #resultQueue.put(task.toDump(), True, 2)
-                resultQueue.put(task.toDump())
-            except Exception as ex:
-                dprint('Taskloop {} EXCEPTION {} '.format(task.mJobId, ex))
-                #resultQueue.put(ex, True)
-                resultQueue.put(ex)
-        elif isinstance(queueObj, str):
-            if queueObj.startswith('LAST'):
-                dprint('Taskloop put FINISHED')
-                #resultQueue.put('FINISHED', True, 2)
-                resultQueue.put('FINISHED')
-                #finishedEvent.set()
-                dprint('Taskloop FINISHED set')
-        else:
-            dprint('Taskloop put UNHANDLED')
-            dprint('Unhandled {} {}'.format(str(queueObj), type(queueObj)))
-
-
+    return task.toDump()
 
 
 
@@ -438,269 +397,66 @@ class LoadingProgress(object):
 class PixelLoader(QObject):
     """
     Loads pixel from raster images
+    Use QgsTaskManager interface in background
     """
-
-    sigPixelLoaded = pyqtSignal([int, int, object],[object])
+    sigPixelLoaded = pyqtSignal(PixelLoaderTask)
     sigLoadingStarted = pyqtSignal()
-    sigLoadingFinished = pyqtSignal(np.timedelta64)
-    sigLoadingCanceled = pyqtSignal()
+    sigLoadingFinished = pyqtSignal()
+
 
     def __init__(self, *args, **kwds):
         super(PixelLoader, self).__init__(*args, **kwds)
-        #self.filesList = []
-        self.mJobId = -1
-        self.mJobProgress = {}
-        #self.mNumberOfProcesses = 2
-        self.mLoadingStartTime = np.datetime64('now','ms')
-
-        #see https://gis.stackexchange.com/questions/35279/multiprocessing-error-in-qgis-with-python-on-windows
-        #path = os.path.abspath(os.path.join(sys.exec_prefix, '../../bin/pythonw.exe'))
-        #assert os.path.exists(path)
-
-        multiprocessing.set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
-        sys.argv = [__file__]
-
-        self.mResultQueue = multiprocessing.Queue(maxsize=0)
-        self.mTaskQueue = multiprocessing.Queue(maxsize=0)
-        self.mCancelEvent = multiprocessing.Event()
-        self.mKillEvent = multiprocessing.Event()
-        self.mWorkerProcess = None
-
-        self.queueCheckTimer = QTimer()  #
-        #self.queueCheckTimer.setInterval(200)
-        self.queueCheckTimer.timeout.connect(self.checkTaskResults)
-        #self.queueCheckTimer.timeout.connect(self.dummySlot)
-        self.queueCheckTimer.start(250)
-
-    def initWorkerProcess(self, id):
-
-        if not isinstance(self.mWorkerProcess, multiprocessing.Process):
-            multiprocessing.set_executable(os.path.join(sys.exec_prefix, 'pythonw.exe'))
-            sys.argv = [__file__]
-
-            self.mWorkerProcess = multiprocessing.Process(name='PixelLoaderWorkingProcess_{}'.format(id),
-                                                          target=pixelLoadingLoop,
-                                                          args=(self.mTaskQueue, self.mResultQueue, self.mCancelEvent, self.mKillEvent))
-
-            self.mWorkerProcess.daemon = True
-            self.mWorkerProcess.start()
-            return True
-        else:
-            if not self.mWorkerProcess.is_alive():
-                dprint('WorkerProcess exit code {}'.format(self.mWorkerProcess.exitcode))
-
-                #self.mWorkerProcess.join(2)
-                self.mWorkerProcess = None
-
-                #code = self.mWorkerProcess.exitcode
-                self.pixelLoadingLoop(self.mTaskQueue, self.mResultQueue, self.mCancelEvent, self.mKillEvent)
-                #self.mWorkerProcess = None
-                return False
-                #self.initWorkerProcess()
-                #self.mWorkerProcess.run()
-            else:
-                return True
+        self.mTasks = {}
 
 
+    def tasks(self)->list:
+        """
+        Returns the list of QgsTaskWrappers
+        :return: list
+        """
 
+        return self.taskManager().tasks()
 
-    def onPixelLoaded(self, dataList):
-        assert isinstance(dataList, list)
-        for data in dataList:
-            assert isinstance(data, PixelLoaderTask)
-
-            if data.mJobId not in self.mJobProgress.keys():
-                return
-            else:
-                progressInfo = self.mJobProgress[data.mJobId]
-
-                assert isinstance(progressInfo, LoadingProgress)
-                if not data.success():
-                    s = ""
-
-                progressInfo.addResult(data.success())
-                if progressInfo.done() == progressInfo.total():
-                    self.mJobProgress.pop(data.mJobId)
-
-                self.sigPixelLoaded[int, int, object].emit(progressInfo.done(), progressInfo.total(), data)
-                self.sigPixelLoaded[object].emit(data)
-
-    #def setNumberOfProcesses(self, nProcesses):
-    #    assert nProcesses >= 1
-    #    self.mNumberOfProcesses = nProcesses
+    def taskManager(self)->QgsTaskManager:
+        return QgsApplication.taskManager()
 
     def startLoading(self, tasks):
 
         assert isinstance(tasks, list)
-        self.sigLoadingStarted.emit()
-        paths = []
-        for t in tasks:
-            assert isinstance(t, PixelLoaderTask)
-            paths.append(t.sourcePath)
-
-        self.mLoadingStartTime = np.datetime64('now', 'ms')
-
-        self.mJobId += 1
-        jobId = self.mJobId
-
-        self.mJobProgress[jobId] = LoadingProgress(jobId, len(tasks))
-
-        #self.mKillEvent.clear()
-        for t in tasks:
-            assert isinstance(t, PixelLoaderTask)
-            t.mJobId = self.mJobId
-            self.mTaskQueue.put(t.toDump())
-        self.mTaskQueue.put('LAST_{}'.format(jobId))
-
-        #self.mWorkerProcess = None
-        t = 0
-        while not self.initWorkerProcess('{}.{}'.format(self.mJobId, t)) and t < 10:
-            t += 1
-        s = ""
-
-
-    def cancelLoading(self):
-        self.mCancelEvent.set()
-
-
-    def isReadyToLoad(self):
-
-        return self.mTaskQueue is None or (self.mTaskQueue.empty() and self.mResultQueue.empty())
-
-
-    def checkTaskResults(self, *args):
-        dataList = []
-        finished = False
-        canceled = False
-        #print('check task results')
-        if isinstance(self.mWorkerProcess, multiprocessing.Process):
-            while not self.mResultQueue.empty():
-                import queue
-                try:
-                    #data = self.mResultQueue.get(True, 2)
-                    data = self.mResultQueue.get()
-                    s = ""
-                except queue.Empty:
-                    break
-
-                if isinstance(data, bytes):
-                    task = PixelLoaderTask.fromDump(data)
-                    dataList.append(task)
-                    dprint('PixelLoader result pulled')
-                elif isinstance(data, str):
-                    if data == 'FINISHED':
-                        finished = True
-                    elif data == 'CANCELED':
-                        canceled = True
-                    else:
-                        s = ""
-                else:
-                    raise Exception('Unhandled type returned {}'.format(data))
-            if len(dataList) > 0:
-                 self.onPixelLoaded(dataList)
-
-            if finished:
-                dt = np.datetime64('now', 'ms') - self.mLoadingStartTime
-                self.sigLoadingFinished.emit(dt)
-
-
-                if self.mTaskQueue.empty() and self.mResultQueue.empty():
-                    pass
-                    #self.mWorkerProcess.terminate()
-                    #self.mWorkerProcess.join()
 
+        tm = self.taskManager()
 
+        #self.sigLoadingStarted.emit()
 
+        #todo: create chuncks
+        import uuid
+        for plt in tasks:
+            assert isinstance(plt, PixelLoaderTask)
 
+            taskName = 'pltTask.{}'.format(uuid.uuid4())
+            plt.setId(taskName)
+            dump = plt.toDump()
+            qgsTask = QgsTask.fromFunction(taskName, doLoaderTask, dump, on_finished=self.onLoadingFinished)
+            tm.addTask(qgsTask)
+            self.mTasks[taskName] = qgsTask
 
 
-if __name__ == '__main__':
+    def onLoadingFinished(self, *args, **kwds):
 
-    from timeseriesviewer.utils import initQgisApplication
-    import example.Images
-    qgsApp = initQgisApplication()
-    from PyQt5.QtGui import *
-    from PyQt5.QtWidgets import *
+        error = args[0]
+        if error is None:
+            dump = args[1]
+            plt = PixelLoaderTask.fromDump(dump)
+            if isinstance(plt, PixelLoaderTask):
+                self.mTasks.pop(plt.id())
+                self.sigPixelLoaded.emit(plt)
 
-    from timeseriesviewer.pixelloader import doLoaderTask, PixelLoaderTask
 
+    def status(self)->tuple:
 
-    gb = QGroupBox()
-    gb.setTitle('Sandbox')
-    DEBUG = False
-
-    import example.Images
-    from timeseriesviewer.utils import file_search
-    dir = os.path.dirname(example.Images.__file__)
-    #files = file_search(dir, '*.tif')
-    files = [example.Images.Img_2014_05_07_LC82270652014127LGN00_BOA]
-    files.append(example.Images.Img_2014_04_29_LE72270652014119CUB00_BOA)
-    files.extend(file_search(dir, 're_*.tif'))
-    for f in files: print(f)
-    ext = SpatialExtent.fromRasterSource(files[0])
-
-    from qgis.core import QgsPoint
-    x,y = ext.center()
-
-    geoms1 = [#SpatialPoint(ext.crs(), 681151.214,-752388.476), #nodata in Img_2014_04_29_LE72270652014119CUB00_BOA
-             SpatialExtent(ext.crs(),x+10000,y,x+12000, y+70 ), #out of image
-             SpatialExtent(ext.crs(),x,y,x+10000, y+70 ),
-             SpatialPoint(ext.crs(), x,y),
-             SpatialPoint(ext.crs(), x+250, y+70)]
-    geoms2 = [  # SpatialPoint(ext.crs(), 681151.214,-752388.476), #nodata in Img_2014_04_29_LE72270652014119CUB00_BOA
-        SpatialPoint(ext.crs(), x - 100, y),
-        SpatialPoint(ext.crs(), x + 50, y + 70)]
-
-
-    def onPxLoaded(*args):
-        n, nmax, task = args
-        assert isinstance(task, PixelLoaderTask)
-        print('Task {} Loaded'.format(task.mJobId))
-        print(task)
-
-    PL = PixelLoader()
-    def onDummy(*args):
-        print(('dummy',args))
-
-    def onTimer(*args):
-        print(('TIMER',PL))
-        pass
-
-    PL.sigPixelLoaded.connect(onPxLoaded)
-    PL.sigLoadingFinished.connect(lambda: onDummy('finished'))
-    #PL.sigLoadingFinished.connect(qgsApp.quit)
-    PL.sigLoadingCanceled.connect(lambda: onDummy('canceled'))
-    PL.sigLoadingStarted.connect(lambda: onDummy('started'))
-    PL.sigPixelLoaded.connect(lambda : onDummy('px loaded'))
-
-    tasks1 = []
-    tasks2 = []
-    for i, f in enumerate(files):
-        kwargs = {'myid':'myID{}'.format(i)}
-        tasks1.append(PixelLoaderTask(f, geoms1, bandIndices=None, **kwargs))
-        tasks2.append(PixelLoaderTask(f, geoms2, bandIndices=None, **kwargs))
-
-    PL.startLoading(tasks1)
-    PL.startLoading(tasks2)
-
-    #QTimer.singleShot(2000, lambda : PL.cancelLoading())
-
-    def addProfile():
-        x0, y1 = ext.upperLeftPt()
-        x1, y0 = ext.lowerRightPt()
-
-        x = x0 + (x1 - x0) * np.random.sample()
-        y = y0 + (y1 - y0) * np.random.sample()
-        pt = SpatialPoint(ext.crs(), x, y)
-        tasks = []
-        for i, f in enumerate(files):
-            tasks.append(PixelLoaderTask(f, [pt], bandIndices=[0,1,3], **kwargs))
-        PL.startLoading(tasks)
+        return None
 
+    def cancelLoading(self):
+        raise NotImplementedError
 
-    btn = QPushButton('Add Profile')
-    btn.clicked.connect(addProfile)
-    btn.show()
 
-    qgsApp.exec_()
-    s = ""
\ No newline at end of file
diff --git a/timeseriesviewer/profilevisualization.py b/timeseriesviewer/profilevisualization.py
index e238a6498787a53af2d347520a6eda3e92fff455..b1bb6cf7867d8286b0f65189b9ae5ca53f278694 100644
--- a/timeseriesviewer/profilevisualization.py
+++ b/timeseriesviewer/profilevisualization.py
@@ -1682,25 +1682,23 @@ class SpectralTemporalVisualization(QObject):
         self.sigMoveToTSD.emit(self.TS[i])
 
 
-    def onPixelLoaded(self, nDone, nMax, d):
-        self.ui.progressBar.setValue(nDone)
-        self.ui.progressBar.setMaximum(nMax)
+    def onPixelLoaded(self, d):
 
-        assert isinstance(d, PixelLoaderTask)
+        if isinstance(d, PixelLoaderTask):
 
-        bn = os.path.basename(d.sourcePath)
-        if d.success():
+            bn = os.path.basename(d.sourcePath)
+            if d.success():
 
-            t = 'Loaded {} pixel from {}.'.format(len(d.resProfiles), bn)
-            self.mTemporalProfileLayer.addPixelLoaderResult(d)
-            self.updateRequested = True
-        else:
-            t = 'Failed loading from {}.'.format(bn)
-            if d.info and d.info != '':
-                t += '({})'.format(d.info)
+                t = 'Loaded {} pixel from {}.'.format(len(d.resProfiles), bn)
+                self.mTemporalProfileLayer.addPixelLoaderResult(d)
+                self.updateRequested = True
+            else:
+                t = 'Failed loading from {}.'.format(bn)
+                if d.info and d.info != '':
+                    t += '({})'.format(d.info)
 
-        # QgsApplication.processEvents()
-        self.ui.progressInfo.setText(t)
+            # QgsApplication.processEvents()
+            self.ui.progressInfo.setText(t)
 
     def requestUpdate(self, *args):
         self.updateRequested = True
@@ -1750,7 +1748,7 @@ class SpectralTemporalVisualization(QObject):
         self.loadCoordinate(spatialPoints=spatialPoints, mode='all', backgroundProcess=backgroundProcess)
 
     LOADING_MODES = ['missing','reload','all']
-    def loadCoordinate(self, spatialPoints=None, LUT_bandIndices=None, mode='missing', backgroundProcess = False):
+    def loadCoordinate(self, spatialPoints=None, LUT_bandIndices=None, mode='missing', backgroundProcess = True):
         """
         :param spatialPoints: [list-of-geometries] to load pixel values from
         :param LUT_bandIndices: dictionary {sensor:[indices]} with band indices to be loaded per sensor
@@ -1858,10 +1856,10 @@ class SpectralTemporalVisualization(QObject):
                 self.pixelLoader.startLoading(tasks)
             else:
                 import timeseriesviewer.pixelloader
-                tasks = [timeseriesviewer.pixelloader.doLoaderTask(task) for task in tasks]
+                tasks = [PixelLoaderTask.fromDump(timeseriesviewer.pixelloader.doLoaderTask(None, task.toDump())) for task in tasks]
                 l = len(tasks)
                 for i, task in enumerate(tasks):
-                    self.pixelLoader.sigPixelLoaded.emit(i+1, l, task)
+                    self.pixelLoader.sigPixelLoaded.emit(task)
 
         else:
             if DEBUG:
@@ -2003,7 +2001,7 @@ def examplePixelLoader():
     gb.setTitle('Sandbox')
 
     PL = PixelLoader()
-    PL.setNumberOfThreads(2)
+
 
     if False:
         files = ['observationcloud/testdata/2014-07-26_LC82270652014207LGN00_BOA.bsq',
diff --git a/timeseriesviewer/temporalprofiles2d.py b/timeseriesviewer/temporalprofiles2d.py
index d89f6f4518a8980e5a6a17b195a15df7deae2704..f8e202f6bff479d6334c065ba484aafe16589e60 100644
--- a/timeseriesviewer/temporalprofiles2d.py
+++ b/timeseriesviewer/temporalprofiles2d.py
@@ -698,7 +698,7 @@ class TemporalProfile(QObject):
 
 
         for task in tasks:
-            result = doLoaderTask(task)
+            result = PixelLoaderTask.fromDump(doLoaderTask(None, task))
             assert isinstance(result, PixelLoaderTask)
             self.pullDataUpdate(result)
 
@@ -1060,7 +1060,7 @@ class TemporalProfileLayer(QgsVectorLayer):
                 LUT_bandIndices[sensor] = list(range(sensor.nb))
 
         PL = PixelLoader()
-        PL.sigPixelLoaded[object].connect(self.addPixelLoaderResult)
+        PL.sigPixelLoaded.connect(self.addPixelLoaderResult)
         # update new / existing points
 
         for tsd in self.mTimeSeries:
@@ -1097,11 +1097,10 @@ class TemporalProfileLayer(QgsVectorLayer):
                 PL.startLoading(tasks)
             else:
                 import timeseriesviewer.pixelloader
-                tasks = [timeseriesviewer.pixelloader.doLoaderTask(task) for task in tasks]
+                tasks = [PixelLoaderTask.fromDump(timeseriesviewer.pixelloader.doLoaderTask(None, task.toDump())) for task in tasks]
                 l = len(tasks)
                 for i, task in enumerate(tasks):
-                    PL.sigPixelLoaded[int, int, object].emit(i + 1, l, task)
-                    PL.sigPixelLoaded[object].emit(task)
+                    PL.sigPixelLoaded.emit(task)
 
 
         else:
@@ -1226,7 +1225,7 @@ class TemporalProfileLayer(QgsVectorLayer):
         #styles.setRowStyles([red])
 
 
-    def createTemporalProfiles(self, coordinates):
+    def createTemporalProfiles(self, coordinates)->list:
         """
         Creates temporal profiles
         :param coordinates:
@@ -1268,7 +1267,7 @@ class TemporalProfileLayer(QgsVectorLayer):
                 p.updateLoadingStatus()
             return profiles
         else:
-            return None
+            return []
 
 
     def saveEdits(self, leaveEditable=True, triggerRepaint=True):
diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py
index 8a4acde2650413596f15b69131a11091b9352e29..c73f3d3eb9a04e992ff9f00b6f29007c9c9abefd 100644
--- a/timeseriesviewer/utils.py
+++ b/timeseriesviewer/utils.py
@@ -20,7 +20,7 @@
 """
 # noinspection PyPep8Naming
 
-import os, sys, math, re, io, fnmatch
+import os, sys, math, re, io, fnmatch, uuid
 
 
 from collections import defaultdict
@@ -255,6 +255,12 @@ class SpatialPoint(QgsPointXY):
         crs = mapCanvas.mapSettings().destinationCrs()
         return SpatialPoint(crs, mapCanvas.center())
 
+    @staticmethod
+    def fromMapLayerCenter(mapLayer:QgsMapLayer):
+        assert isinstance(mapLayer, QgsMapLayer) and mapLayer.isValid()
+        crs = mapLayer.crs()
+        return SpatialPoint(crs, mapLayer.extent().center())
+
     @staticmethod
     def fromSpatialExtent(spatialExtent):
         assert isinstance(spatialExtent, SpatialExtent)
@@ -1092,15 +1098,12 @@ def zipdir(pathDir, pathZip):
                     zip.write(filename, arcname)
 
 
-def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgisResourceDir=None):
+
+def initQgisApplication(PATH_QGIS=None, qgisDebug=False, qgisResourceDir=None):
     """
     Initializes the QGIS Environment
     :return: QgsApplication instance of local QGIS installation
     """
-    if pythonPlugins is None:
-        pythonPlugins = []
-    assert isinstance(pythonPlugins, list)
-
     if os.path.exists(os.path.join(DIR_REPO, 'qgisresources')):
         qgisResourceDir = os.path.join(DIR_REPO, 'qgisresources')
     if isinstance(qgisResourceDir, str):
@@ -1113,24 +1116,11 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi
             if "qInitResources" in dir(mod):
                 mod.qInitResources()
 
-    envVar = os.environ.get('QGIS_PLUGINPATH', None)
-    if isinstance(envVar, list):
-        pythonPlugins.extend(re.split('[;:]', envVar))
-
-    # make plugin paths available to QGIS and Python
-    os.environ['QGIS_PLUGINPATH'] = ';'.join(pythonPlugins)
-    os.environ['QGIS_DEBUG'] = '1' if qgisDebug else '0'
-    for p in pythonPlugins:
-        sys.path.append(p)
-
     if isinstance(QgsApplication.instance(), QgsApplication):
-
         return QgsApplication.instance()
-
     else:
-
         if PATH_QGIS is None:
-            # find QGIS Path
+            # find QGIS_PREFIX_PATH
             if sys.platform == 'darwin':
                 # search for the QGIS.app
                 import qgis, re
@@ -1148,12 +1138,17 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi
             else:
                 # assume OSGeo4W startup
                 PATH_QGIS = os.environ['QGIS_PREFIX_PATH']
-
         assert os.path.exists(PATH_QGIS)
 
-        qgsApp = QgsApplication([], True)
-        qgsApp.setPrefixPath(PATH_QGIS, True)
-        qgsApp.initQgis()
+        try:
+            import qgis.testing
+            qgsApp = qgis.testing.start_app()
+        except Exception as ex:
+            print(ex)
+
+            qgsApp = QgsApplication([], True)
+            qgsApp.setPrefixPath(PATH_QGIS, True)
+            qgsApp.initQgis()
 
         def printQgisLog(msg, tag, level):
             if tag not in ['Processing']:
@@ -1175,7 +1170,17 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi
 
 
 
+
 class TestObjects():
+    @staticmethod
+    def createTestImageSeries(n=1) -> list:
+        assert n > 0
+
+        datasets = []
+        for i in range(n):
+            ds = TestObjects.inMemoryImage()
+            datasets.append(ds)
+        return datasets
 
     @staticmethod
     def inMemoryImage(nl=10, ns=20, nb=3, crs='EPSG:32632')->gdal.Dataset:
@@ -1189,8 +1194,8 @@ class TestObjects():
         """
         drv = gdal.GetDriverByName('GTiff')
         assert isinstance(drv, gdal.Driver)
-
-        path = '/vsimem/testimage.tif'
+        id = uuid.uuid4()
+        path = '/vsimem/testimage.multiband.{}.tif'.format(id)
         ds = drv.Create(path, ns, nl, bands=nb, eType=gdal.GDT_Float32)
 
         if isinstance(crs, str):
@@ -1217,11 +1222,12 @@ class TestObjects():
         scheme = ClassificationScheme()
         scheme.createClasses(n)
 
-        drv = gdal.GetDriverByName('MEM')
+        drv = gdal.GetDriverByName('GTiff')
         assert isinstance(drv, gdal.Driver)
 
-
-        ds = drv.Create('', ns, nl, bands=nb, eType=gdal.GDT_Byte)
+        id = uuid.uuid4()
+        path = '/vsimem/testimage.class._{}.tif'.format(id)
+        ds = drv.Create(path, ns, nl, bands=nb, eType=gdal.GDT_Byte)
 
         if isinstance(crs, str):
             c = QgsCoordinateReferenceSystem(crs)