diff --git a/eotimeseriesviewer/externals/qps/enmap.tif b/eotimeseriesviewer/externals/qps/enmap.tif
index dd38499ac4bb8ce9d97bce3322873de8052db411..1f1efbe42830587a7c094b3a8c0642f9dfe25380 100644
Binary files a/eotimeseriesviewer/externals/qps/enmap.tif and b/eotimeseriesviewer/externals/qps/enmap.tif differ
diff --git a/eotimeseriesviewer/externals/qps/speclib/core.py b/eotimeseriesviewer/externals/qps/speclib/core.py
index da93a76481985a37264e223c5e4af3e3516c7b85..5c7987d5c295b2a2fedaf5e2b364b0525f54fa55 100644
--- a/eotimeseriesviewer/externals/qps/speclib/core.py
+++ b/eotimeseriesviewer/externals/qps/speclib/core.py
@@ -35,7 +35,7 @@ import uuid
 from osgeo import osr
 from ..speclib import SpectralLibrarySettingsKey
 from PyQt5.QtWidgets import *
-from qgis.core import \
+from qgis.core import QgsApplication, \
     QgsRenderContext, QgsFeature, QgsVectorLayer, QgsMapLayer, QgsRasterLayer, \
     QgsAttributeTableConfig, QgsField, QgsFields, QgsCoordinateReferenceSystem, QgsCoordinateTransform, \
     QgsVectorFileWriter, QgsActionManager, QgsFeatureIterator, QgsFeatureRequest, \
@@ -43,7 +43,7 @@ from qgis.core import \
     QgsRaster, QgsDefaultValue, QgsReadWriteContext, \
     QgsCategorizedSymbolRenderer, QgsMapLayerProxyModel, \
     QgsSymbol, QgsNullSymbolRenderer, QgsMarkerSymbol, QgsLineSymbol, QgsFillSymbol, \
-    QgsEditorWidgetSetup, QgsAction, QgsTask, QgsMessageLog
+    QgsEditorWidgetSetup, QgsAction, QgsTask, QgsMessageLog, QgsFileUtils
 
 from qgis.gui import \
     QgsGui, QgsMapCanvas, QgsDualView, QgisInterface, QgsEditorConfigWidget, \
@@ -1388,10 +1388,10 @@ class SpectralProfileRenderer(object):
                         pass
                     else:
                         style.setVisibility(False)
-                    #symbol = renderer.sourceSymbol()
+                    # symbol = renderer.sourceSymbol()
                 elif isinstance(symbol, (QgsMarkerSymbol, QgsLineSymbol, QgsFillSymbol)):
                     color: QColor = symbol.color()
-                    color.setAlpha(int(symbol.opacity()*100))
+                    color.setAlpha(int(symbol.opacity() * 100))
 
                     style.setLineColor(color)
                     style.setMarkerColor(color)
@@ -2157,8 +2157,8 @@ class SpectralLibrary(QgsVectorLayer):
                  path: str = None,
                  baseName: str = DEFAULT_NAME,
                  options: QgsVectorLayer.LayerOptions = None,
-                 uri: str = None, # deprectated
-                 name: str = None # deprectated
+                 uri: str = None,  # deprectated
+                 name: str = None  # deprectated
                  ):
 
         if isinstance(uri, str):
@@ -2623,10 +2623,10 @@ class SpectralLibrary(QgsVectorLayer):
         msg = super(SpectralLibrary, self).exportNamedStyle(doc, context=context, categories=categories)
         if msg == '':
             qgsNode = doc.documentElement().toElement()
-            #speclibNode = doc.createElement(XMLNODE_PROFILE_RENDERER)
+            # speclibNode = doc.createElement(XMLNODE_PROFILE_RENDERER)
             if isinstance(self.mProfileRenderer, SpectralProfileRenderer):
                 self.mProfileRenderer.writeXml(qgsNode, doc)
-            #qgsNode.appendChild(speclibNode)
+            # qgsNode.appendChild(speclibNode)
 
         return msg
 
@@ -2648,7 +2648,8 @@ class SpectralLibrary(QgsVectorLayer):
         warnings.warn('Use SpectralLibrary.write() instead', DeprecationWarning)
         return self.write(*args, **kwds)
 
-    def writeRasterImages(self, pathOne: typing.Union[str, pathlib.Path], drv:str='GTiff') -> typing.List[pathlib.Path]:
+    def writeRasterImages(self, pathOne: typing.Union[str, pathlib.Path], drv: str = 'GTiff') -> typing.List[
+        pathlib.Path]:
         """
         Writes the SpectralLibrary into images of same spectral properties
         :return: list of image paths
@@ -2659,7 +2660,7 @@ class SpectralLibrary(QgsVectorLayer):
         basename, ext = os.path.splitext(pathOne.name)
 
         assert pathOne.parent.is_dir()
-        results = []
+        imageFiles = []
         for k, profiles in self.groupBySpectralProperties().items():
             xValues, xUnit, yUnit = k
             ns: int = len(profiles)
@@ -2667,26 +2668,29 @@ class SpectralLibrary(QgsVectorLayer):
 
             ref_profile = np.asarray(profiles[0].yValues())
             dtype = ref_profile.dtype
-            imageArray = np.empty((nb, ns, 1), dtype=dtype)
-            imageArray[:,0,0] = ref_profile
+            imageArray = np.empty((nb, 1, ns), dtype=dtype)
+            imageArray[:, 0, 0] = ref_profile
+
             for i in range(1, len(profiles)):
-                imageArray[:, i, 0] = np.asarray(profiles[i].yValues(), dtype=dtype)
-            if len(results) == 0:
+                imageArray[:, 0, i] = np.asarray(profiles[i].yValues(), dtype=dtype)
+
+            if len(imageFiles) == 0:
                 pathDst = pathOne.parent / f'{basename}{ext}'
             else:
-                pathDst = pathOne.parent / f'{basename}{i}{ext}'
+                pathDst = pathOne.parent / f'{basename}{len(imageFiles)}{ext}'
 
             dsDst: gdal.Dataset = gdal_array.SaveArray(imageArray, pathDst.as_posix(), format=drv)
             fakeProjection: osr.SpatialReference = osr.SpatialReference()
             fakeProjection.SetFromUserInput('EPSG:3857')
             dsDst.SetProjection(fakeProjection.ExportToWkt())
-            dsDst.SetGeoTransform([0.0, 1.0, 0.0, 0.0, 0.0, -1.0])
+            # north-up project, 1 px above equator, starting at 0°, n pixels = n profiles towards east
+            dsDst.SetGeoTransform([0.0, 1.0, 0.0, 1.0, 0.0, -1.0])
             dsDst.SetMetadataItem('wavelength units', xUnit)
             dsDst.SetMetadataItem('wavelength', ','.join(f'{v}' for v in xValues))
             dsDst.FlushCache()
-            results.append(pathDst)
+            imageFiles.append(pathDst)
             del dsDst
-        return results
+        return imageFiles
 
     def write(self, path: str, **kwds) -> typing.List[str]:
         """
@@ -2703,7 +2707,7 @@ class SpectralLibrary(QgsVectorLayer):
         if path is None:
             path, filter = QFileDialog.getSaveFileName(parent=kwds.get('parent'),
                                                        caption='Save Spectral Library',
-                                                       directory='speclib',
+                                                       directory=QgsFileUtils.stringToSafeFilename(self.name()),
                                                        filter=FILTERS)
 
         if isinstance(path, pathlib.Path):
@@ -2724,41 +2728,46 @@ class SpectralLibrary(QgsVectorLayer):
 
         return []
 
-    def yRange(self):
-        profiles = self.profiles()
-        minY = min([min(p.yValues()) for p in profiles])
-        maxY = max([max(p.yValues()) for p in profiles])
+    def yRange(self) -> typing.List[float]:
+        """
+        Returns the maximum y range
+        :return:
+        :rtype:
+        """
+
+        minY = maxY = 0
+
+        for p in self.profiles():
+            yValues = p.yValues()
+            minY = min(minY, min(yValues))
+            maxY = max(maxY, max(yValues))
+
         return minY, maxY
 
     def __repr__(self):
         return str(self.__class__) + '"{}" {} feature(s)'.format(self.name(), self.dataProvider().featureCount())
 
-    def plot(self):
+    def plot(self) -> QWidget:
         """Create a plot widget and shows all SpectralProfile in this SpectralLibrary."""
-        from ..externals import pyqtgraph as pg
-        pg.mkQApp()
 
-        win = pg.GraphicsWindow(title="Spectral Library")
-        win.resize(1000, 600)
+        app = None
+        if not isinstance(QgsApplication.instance(), QgsApplication):
+            from ..testing import start_app
+            app = start_app()
 
-        # Enable antialiasing for prettier plots
-        pg.setConfigOptions(antialias=True)
+        from .gui import SpectralLibraryWidget
 
-        # Create a plot with some random data
-        p1 = win.addPlot(title="Spectral Library {}".format(self.name()), pen=0.5)
-        yMin, yMax = self.yRange()
-        p1.setYRange(yMin, yMax)
+        w = SpectralLibraryWidget(speclib=self)
+        w.show()
 
-        # Add three infinite lines with labels
-        for p in self:
-            pi = pg.PlotDataItem(p.xValues(), p.yValues())
-            p1.addItem(pi)
+        if app:
+            app.exec_()
 
-        pg.QAPP.exec_()
+        return w
 
     def fieldNames(self) -> list:
         """
-        Retunrs the field names. Shortcut from self.fields().names()
+        Returns the field names. Shortcut from self.fields().names()
         :return: [list-of-str]
         """
         return self.fields().names()
diff --git a/eotimeseriesviewer/externals/qps/speclib/gui.py b/eotimeseriesviewer/externals/qps/speclib/gui.py
index 57d87f8a78b2ba03069536ee72037705e7691fe4..dcfd60224fbd099ace691f09900e40174328b343 100644
--- a/eotimeseriesviewer/externals/qps/speclib/gui.py
+++ b/eotimeseriesviewer/externals/qps/speclib/gui.py
@@ -42,7 +42,7 @@ from qgis.core import \
     QgsFeature, QgsRenderContext, QgsNullSymbolRenderer, \
     QgsRasterLayer, QgsMapLayer, QgsVectorLayer, \
     QgsSymbol, QgsMarkerSymbol, QgsLineSymbol, QgsFillSymbol, \
-    QgsAttributeTableConfig, QgsField, QgsMapLayerProxyModel
+    QgsAttributeTableConfig, QgsField, QgsMapLayerProxyModel, QgsFileUtils
 from qgis.gui import \
     QgsEditorWidgetWrapper, QgsAttributeTableView, \
     QgsActionMenu, QgsEditorWidgetFactory, QgsStatusBar, \
@@ -808,8 +808,7 @@ class SpectralLibraryPlotWidget(pg.PlotWidget):
         self.mXAxis: SpectralXAxis = pi.getAxis('bottom')
         assert isinstance(self.mXAxis, SpectralXAxis)
 
-        self.mSpeclib: SpectralLibrary
-        self.mSpeclib = None
+        self.mSpeclib: SpectralLibrary = None
         self.mSpeclibSignalConnections = []
 
         self.mXUnitInitialized = False
@@ -2155,7 +2154,7 @@ class SpectralLibraryWidget(AttributeTableWidget):
         from .io.specchio import SPECCHIOSpectralLibraryIO
         from .io.artmo import ARTMOSpectralLibraryIO
         from .io.vectorsources import VectorSourceSpectralLibraryIO
-
+        from .io.rastersources import RasterSourceSpectralLibraryIO
         self.mSpeclibIOInterfaces = [
             EnviSpectralLibraryIO(),
             CSVSpectralLibraryIO(),
@@ -2164,6 +2163,7 @@ class SpectralLibraryWidget(AttributeTableWidget):
             EcoSISSpectralLibraryIO(),
             SPECCHIOSpectralLibraryIO(),
             VectorSourceSpectralLibraryIO(),
+            RasterSourceSpectralLibraryIO(),
         ]
 
         self.mSpeclibIOInterfaces = sorted(self.mSpeclibIOInterfaces, key=lambda c: c.__class__.__name__)
@@ -2233,6 +2233,7 @@ class SpectralLibraryWidget(AttributeTableWidget):
         self.actionExportSpeclib = QAction('Export Spectral Profiles')
         self.actionExportSpeclib.setToolTip('Export spectral profiles to other data formats')
         self.actionExportSpeclib.setIcon(QIcon(':/qps/ui/icons/speclib_save.svg'))
+
         m = QMenu()
         self.createSpeclibExportMenu(m)
         self.actionExportSpeclib.setMenu(m)
@@ -2337,17 +2338,39 @@ class SpectralLibraryWidget(AttributeTableWidget):
         """
         :return: QMenu with QActions and submenus to import SpectralProfiles
         """
+        separated = []
+        from .io.rastersources import RasterSourceSpectralLibraryIO
+
         for iface in self.mSpeclibIOInterfaces:
             assert isinstance(iface, AbstractSpectralLibraryIO), iface
-            iface.addImportActions(self.speclib(), menu)
+            if isinstance(iface, RasterSourceSpectralLibraryIO):
+                separated.append(iface)
+            else:
+                iface.addImportActions(self.speclib(), menu)
+
+        if len(separated) > 0:
+            menu.addSeparator()
+            for iface in separated:
+                iface.addImportActions(self.speclib(), menu)
 
     def createSpeclibExportMenu(self, menu: QMenu):
         """
-        :return: QMenu with QActions and submenus to export SpectralProfiles
+        :return: QMenu with QActions and submenus to export the SpectralLibrary
         """
+        separated = []
+        from .io.rastersources import RasterSourceSpectralLibraryIO
         for iface in self.mSpeclibIOInterfaces:
             assert isinstance(iface, AbstractSpectralLibraryIO)
-            iface.addExportActions(self.speclib(), menu)
+            if isinstance(iface, RasterSourceSpectralLibraryIO):
+                separated.append(iface)
+            else:
+                iface.addExportActions(self.speclib(), menu)
+
+        if len(separated) > 0:
+            menu.addSeparator()
+            for iface in separated:
+                iface.addExportActions(self.speclib(), menu)
+
 
     def plotWidget(self) -> SpectralLibraryPlotWidget:
         return self.mPlotWidget
@@ -2529,7 +2552,7 @@ class SpectralLibraryWidget(AttributeTableWidget):
             self.speclib().startEditing()
 
     def onExportSpectra(self, *args):
-        files = self.mSpeclib.write(None)
+        files = self.speclib().write(None)
         if len(files) > 0:
             self.sigFilesCreated.emit(files)
 
diff --git a/eotimeseriesviewer/externals/qps/speclib/io/rastersources.py b/eotimeseriesviewer/externals/qps/speclib/io/rastersources.py
index 48129861ebaf70e6cda0770b776b3ab42479e020..5e89f7926291725eeb4268d2bb42f20a3c278029 100644
--- a/eotimeseriesviewer/externals/qps/speclib/io/rastersources.py
+++ b/eotimeseriesviewer/externals/qps/speclib/io/rastersources.py
@@ -24,19 +24,21 @@
     along with this software. If not, see <http://www.gnu.org/licenses/>.
 ***************************************************************************
 """
-
+import os
 import sys
 import typing
-
+from osgeo import gdal
+import numpy as np
 from qgis.PyQt import sip
 from qgis.PyQt.QtGui import *
 from qgis.PyQt.QtWidgets import *
 from qgis.PyQt.QtCore import *
 from qgis.core import QgsTask, QgsMapLayer, QgsVectorLayer, QgsRasterLayer, QgsWkbTypes, \
-    QgsTaskManager, QgsMapLayerProxyModel, QgsApplication
-from ..core import SpectralProfile, SpectralLibrary
-from ...utils import SelectMapLayersDialog
+    QgsTaskManager, QgsMapLayerProxyModel, QgsApplication, QgsFileUtils
+from ..core import SpectralProfile, SpectralLibrary, AbstractSpectralLibraryIO, ProgressHandler
+from ...utils import SelectMapLayersDialog, gdalDataset, parseWavelength, parseFWHM, parseBadBandList
 
+PIXEL_LIMIT = 100*100
 
 class SpectralProfileLoadingTask(QgsTask):
 
@@ -240,3 +242,133 @@ class SpectralProfileImportPointsDialog(SelectMapLayersDialog):
         :return: QgsVectorLayer
         """
         return self.mapLayers()[1]
+
+
+class RasterSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
+    """
+    I/O Interface for Raster files.
+    """
+
+    @classmethod
+    def canRead(cls, path: str) -> bool:
+        """
+        Returns true if it can read the source defined by path
+        :param path: source uri
+        :return: True, if source is readable.
+        """
+        path = str(path)
+        try:
+            ds = gdalDataset(path)
+            return True
+        except:
+            return False
+        return False
+
+    @classmethod
+    def readFrom(cls, path,
+                 progressDialog: typing.Union[QProgressDialog, ProgressHandler] = None,
+                 addAttributes: bool = True) -> SpectralLibrary:
+
+        ds: gdal.Dataset = gdalDataset(path)
+        if not isinstance(ds, gdal.Dataset):
+            return None
+
+        speclib = SpectralLibrary()
+        assert isinstance(speclib, SpectralLibrary)
+        sourcepath = ds.GetDescription()
+        basename = os.path.basename(ds.GetDescription())
+        speclib.setName(basename)
+        assert speclib.startEditing()
+
+        wl, wlu = parseWavelength(ds)
+        fwhm = parseFWHM(ds)
+        bbl = parseBadBandList(ds)
+
+        # each none-masked pixel is a profile
+        array = ds.ReadAsArray()
+
+        ref_band: gdal.Band = ds.GetRasterBand(1)
+        no_data = ref_band.GetNoDataValue()
+        valid = np.isfinite(array[0, :])
+        if no_data:
+            valid = valid * (array[0, :] != no_data)
+        valid = np.where(valid)
+
+        n_profiles = len(valid[0])
+        if n_profiles > PIXEL_LIMIT:
+            raise Exception(f'Number of raster image pixels {n_profiles} exceeds PIXEL_LIMIT {PIXEL_LIMIT}')
+
+        if wl is not None:
+            xvalues = wl.tolist()
+        else:
+            xvalues = (np.arange(ds.RasterCount) + 1).tolist()
+
+        profiles = []
+        for y, x in zip(*valid):
+            yvalues = array[:, y, x]
+            p = SpectralProfile(fields=speclib.fields())
+            p.setName(f'Profile {x},{y}')
+            p.setSource(basename)
+            p.setValues(xvalues, yvalues, xUnit=wlu)
+            profiles.append(p)
+        speclib.addProfiles(profiles)
+        speclib.commitChanges()
+        return speclib
+
+    @classmethod
+    def write(cls, speclib: SpectralLibrary,
+              path: str,
+              progressDialog: typing.Union[QProgressDialog, ProgressHandler] = None):
+        """
+        Writes the SpectralLibrary to path and returns a list of written files that can be used to open the spectral library with readFrom
+        """
+        speclib.writeRasterImages(path)
+
+        return [path]
+
+    @classmethod
+    def addImportActions(cls, spectralLibrary: SpectralLibrary, menu: QMenu) -> list:
+
+        def read(speclib: SpectralLibrary):
+
+            path, filter = QFileDialog.getOpenFileName(caption='Raster Image',
+                                                       filter='All types (*.*)')
+            if os.path.isfile(path):
+
+                if not RasterSourceSpectralLibraryIO.canRead(path):
+                    QMessageBox.critical(None, 'Raster image as SpectralLibrary', f'Unable to reads {path}')
+
+                try:
+                    sl = RasterSourceSpectralLibraryIO.readFrom(path)
+                    if isinstance(sl, SpectralLibrary):
+                        speclib.startEditing()
+                        speclib.beginEditCommand('Add Spectral Library from {}'.format(path))
+                        speclib.addSpeclib(sl, addMissingFields=True)
+                        speclib.endEditCommand()
+                        speclib.commitChanges()
+                except Exception as ex:
+                    QMessageBox.critical(None, 'Raster image as SpectralLibrary', str(ex))
+                    return
+        m = menu.addAction('Raster Image')
+        m.setToolTip('Import all pixels as spectral profiles which are not masked. '
+                     'Use careful and not with large images!')
+        m.triggered.connect(lambda *args, sl=spectralLibrary: read(sl))
+
+    @classmethod
+    def addExportActions(cls, spectralLibrary: SpectralLibrary, menu: QMenu) -> list:
+
+        def write(speclib: SpectralLibrary):
+            # https://gdal.org/drivers/vector/index.html
+            LUT_Files = {'GeoTiff (*.tif)': 'GTiff',
+                         'ENVI Raster (*.bsq)': 'ENVI',
+                        }
+
+            path, filter = QFileDialog.getSaveFileName(caption='Write as raster image',
+                                                       filter=';;'.join(LUT_Files.keys()),
+                                                       directory=QgsFileUtils.stringToSafeFilename(speclib.name()))
+            if isinstance(path, str) and len(path) > 0:
+                speclib.writeRasterImages(path, drv=LUT_Files.get(filter, 'GTiff'))
+
+        a = menu.addAction('Raster Image')
+        a.setToolTip('Write profiles as raster image(s), grouped by wavelengths.')
+        a.triggered.connect(lambda *args, sl=spectralLibrary: write(sl))
diff --git a/eotimeseriesviewer/externals/qps/speclib/io/vectorsources.py b/eotimeseriesviewer/externals/qps/speclib/io/vectorsources.py
index 4d424bbbca66a9807963be57bb9747602b3eacd0..477d944e3870d3876fe452b2c7e1a9667cea7c97 100644
--- a/eotimeseriesviewer/externals/qps/speclib/io/vectorsources.py
+++ b/eotimeseriesviewer/externals/qps/speclib/io/vectorsources.py
@@ -34,9 +34,9 @@ import typing
 
 from qgis.PyQt.QtCore import *
 from qgis.PyQt.QtWidgets import *
-from qgis.core import *
-from qgis.gui import *
-from qgis.core import QgsField, QgsVectorLayer, QgsVectorFileWriter, QgsProviderRegistry, QgsProject
+
+from qgis.core import QgsField, QgsVectorLayer, QgsVectorFileWriter, QgsProviderRegistry, \
+    QgsProject, QgsProviderMetadata, QgsFileUtils
 
 from ..core import SpectralProfile, SpectralLibrary, AbstractSpectralLibraryIO, \
     decodeProfileValueDict, encodeProfileValueDict, \
@@ -82,8 +82,7 @@ class VectorSourceFieldValueConverter(QgsVectorFileWriter.FieldValueConverter):
 
 class VectorSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
     """
-    I/O Interface for the EcoSIS spectral library format.
-    See https://ecosis.org for details.
+    I/O Interface for Vector File Formats.
     """
 
     @classmethod
@@ -118,6 +117,8 @@ class VectorSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
                  addAttributes: bool = True) -> SpectralLibrary:
         """
         Returns the SpectralLibrary read from "path"
+        :param progressDialog:
+        :type progressDialog:
         :param path: source of SpectralLibrary
         :return: SpectralLibrary
         """
@@ -252,7 +253,9 @@ class VectorSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
         def read(speclib: SpectralLibrary):
 
             path, filter = QFileDialog.getOpenFileName(caption='Vector File',
-                                                       filter='All type (*.*)')
+                                                       filter='All type (*.*)',
+                                                       directory=QgsFileUtils.stringToSafeFilename(speclib.name())
+                                                       )
             if os.path.isfile(path) and VectorSourceSpectralLibraryIO.canRead(path):
                 sl = VectorSourceSpectralLibraryIO.readFrom(path)
                 if isinstance(sl, SpectralLibrary):
@@ -269,7 +272,7 @@ class VectorSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
 
     @classmethod
     def addExportActions(cls, spectralLibrary: SpectralLibrary, menu: QMenu) -> list:
-
+        """
         def write_new(speclib: SpectralLibrary):
             # this is not available in Python. Why?
             from qgis.gui import QgsVectorLayerSaveAsDialog
@@ -282,7 +285,7 @@ class VectorSourceSpectralLibraryIO(AbstractSpectralLibraryIO):
 
             d = QgsVectorLayerSaveAsDialog(speclib, options=options)
             d.show()
-            s = ""
+        """
 
         def write(speclib: SpectralLibrary):
             # https://gdal.org/drivers/vector/index.html
diff --git a/eotimeseriesviewer/externals/qps/testing.py b/eotimeseriesviewer/externals/qps/testing.py
index ec3ea1235779a9a421fcc56b650170e490a39a28..6b645d4e503a44b2f5ed562839894badc10726d5 100644
--- a/eotimeseriesviewer/externals/qps/testing.py
+++ b/eotimeseriesviewer/externals/qps/testing.py
@@ -129,7 +129,8 @@ def start_app(cleanup=True, options=StartOptions.Minimized, resources: list = []
                 qgsApp.addLibraryPath(candidate.as_posix())
 
     assert QgsProviderRegistry.instance().libraryDirectory().exists(), \
-        'Directory: {} does not exist'.format(QgsProviderRegistry.instance().libraryDirectory().path())
+        'Directory: {} does not exist. Please check if QGIS_PREFIX_PATH correct'.format(
+            QgsProviderRegistry.instance().libraryDirectory().path())
 
     # initiate a PythonRunner instance if None exists
     if StartOptions.PythonRunner in options and not QgsPythonRunner.isValid():
@@ -518,9 +519,16 @@ class TestObjects():
         coredata, wl, wlu, gt, wkt = TestObjects.coreData()
         cnb, cnl, cns = coredata.shape
         assert n > 0
+        if not isinstance(n_bands, list):
+            n_bands = [n_bands]
         assert isinstance(n_bands, list)
-        for nb in n_bands:
-            assert nb == -1 or nb > 0 and nb <= cnb
+        for i in range(len(n_bands)):
+            nb = n_bands[i]
+            if nb == -1:
+                n_bands[i] = cnb
+            else:
+                assert 0 < nb <= cnb, f'Number of bands need to be in range 0 < nb <= {cnb}.'
+
         n_bands = [nb if nb > 0 else cnb for nb in n_bands]
 
         for nb in n_bands:
@@ -544,7 +552,7 @@ class TestObjects():
         for (data, wl, data_wlu) in TestObjects.spectralProfileData(n, n_bands=n_bands):
             if wlu is None:
                 wlu = data_wlu
-            if wlu != data_wlu:
+            elif wlu != data_wlu:
                 wl = UnitLookup.convertMetricUnit(wl, data_wlu, wlu)
 
             profile = SpectralProfile(fields=fields)
@@ -564,6 +572,8 @@ class TestObjects():
                               wlu: str = None):
         """
         Creates an Spectral Library
+        :param n_bands:
+        :type n_bands:
         :param wlu:
         :type wlu:
         :param n: total number of profiles
@@ -717,7 +727,9 @@ class TestObjects():
                 wl = core_wl[:nb].tolist()
             assert len(wl) == nb
 
-            if wlu != core_wlu:
+            if wlu is None:
+                wlu = core_wlu
+            elif wlu != core_wlu:
                 wl = UnitLookup.convertMetricUnit(wl, core_wlu, wlu)
 
             domain = None
diff --git a/tests/test_stackedbandinput.py b/tests/test_stackedbandinput.py
index f7e87a2eaee27188b12f960969ea31871a82e138..a2884764e19ccaf7e3f437f099c8583594563f67 100644
--- a/tests/test_stackedbandinput.py
+++ b/tests/test_stackedbandinput.py
@@ -19,20 +19,18 @@
 # noinspection PyPep8Naming
 import os
 import sys
+import unittest
 import xmlrunner
+from qgis.PyQt.QtWidgets import QApplication
+from qgis.core import QgsRasterLayer
 from eotimeseriesviewer.tests import start_app, EOTSVTestCase
 from eotimeseriesviewer.utils import nextColor
-from osgeo import gdal_array
-from qgis.PyQt.QtGui import *
-from qgis.PyQt.QtCore import *
-import unittest, tempfile
-
-
-
+from osgeo import gdal_array, osr
 from eotimeseriesviewer.stackedbandinput import *
-from example.Images import Img_2014_06_16_LE72270652014167CUB00_BOA, Img_2014_05_07_LC82270652014127LGN00_BOA
+
 from eotimeseriesviewer.main import EOTimeSeriesViewer
 
+
 class TestStackedInputs(EOTSVTestCase):
 
     def setUp(self):
@@ -42,7 +40,6 @@ class TestStackedInputs(EOTSVTestCase):
             eotsv.close()
             QApplication.processEvents()
 
-
     def createTestDatasets(self):
 
         vsiDir = r'/vsimem/tmp'
@@ -60,7 +57,7 @@ class TestStackedInputs(EOTSVTestCase):
         assert isinstance(drv, gdal.Driver)
         datasets = []
         for i, r in enumerate([r1, r2]):
-            p = '{}tmpstack{}.bsq'.format(vsiDir, i+1)
+            p = '{}tmpstack{}.bsq'.format(vsiDir, i + 1)
             nb = len(r)
             ds = drv.Create(p, ns, nl, nb, eType=gdal.GDT_Float32)
             assert isinstance(ds, gdal.Dataset)
@@ -74,33 +71,32 @@ class TestStackedInputs(EOTSVTestCase):
             for b, date in enumerate(r):
                 decimalYear = date2num(date)
 
-                band = ds.GetRasterBand(b+1)
+                band = ds.GetRasterBand(b + 1)
                 assert isinstance(band, gdal.Band)
                 band.Fill(decimalYear)
             ds.FlushCache()
             datasets.append(p)
 
-
             if i == 0:
-                #create a classification image stack
+                # create a classification image stack
 
                 nc = nb
                 data = np.ones((nb, ns, nl), dtype=np.uint8)
                 classNames = ['unclassified']
                 colorTable = gdal.ColorTable()
-                colorTable.SetColorEntry(0, (0,0,0))
+                colorTable.SetColorEntry(0, (0, 0, 0))
                 assert isinstance(colorTable, gdal.ColorTable)
                 color = QColor('green')
 
                 for j, date in enumerate(r):
-                    c = j+1
+                    c = j + 1
                     data[j, j:-1, 0:j] = c
                     classNames.append('Class {}'.format(date))
                     colorTable.SetColorEntry(c, color.getRgb())
                     color = nextColor(color)
 
                 p = '{}tmpClassificationStack.bsq'.format(vsiDir)
-                ds = gdal_array.SaveArray(data,p, format='ENVI', prototype=datasets[0])
+                ds = gdal_array.SaveArray(data, p, format='ENVI', prototype=datasets[0])
                 ds.GetRasterBand(1).SetColorTable(colorTable)
                 ds.GetRasterBand(1).SetCategoryNames(classNames)
 
@@ -110,7 +106,6 @@ class TestStackedInputs(EOTSVTestCase):
                 datasets.append(ds)
         return datasets
 
-
     def test_FORCEStacks(self):
 
         pathStack = r'T:\4BJ\2018-2018_000-000_LEVEL4_TSA_SEN2L_EVI_C0_S0_TSS.tif'
@@ -122,7 +117,6 @@ class TestStackedInputs(EOTSVTestCase):
 
             self.showGui()
 
-
     def test_inputmodel(self):
         testData = self.createTestDatasets()
         m = InputStackTableModel()
@@ -135,7 +129,6 @@ class TestStackedInputs(EOTSVTestCase):
         self.assertTrue(len(dIntersecton) > 0)
         self.assertTrue(len(dTotal) > len(dIntersecton))
 
-
     def test_outputmodel(self):
 
         m = OutputImageModel()
@@ -168,7 +161,6 @@ class TestStackedInputs(EOTSVTestCase):
         eTree = m.vrtXML(outInfo, asElementTree=True)
         self.assertIsInstance(eTree, ElementTree.Element)
 
-
     def test_dialog(self):
         d = StackedBandInputDialog()
         d.addSources(self.createTestDatasets())
@@ -195,8 +187,6 @@ class TestStackedInputs(EOTSVTestCase):
 
     def test_withTSV(self):
 
-
-
         testImages = self.createTestDatasets()
         from eotimeseriesviewer.main import EOTimeSeriesViewer
         TSV = EOTimeSeriesViewer()