diff --git a/test/test_spectrallibraries.py b/test/test_spectrallibraries.py
index e99f35d94594c30f2981b915e7ed8d395a4dd2d3..8861e68d979434d8f0c00321b9c856223aa6582f 100644
--- a/test/test_spectrallibraries.py
+++ b/test/test_spectrallibraries.py
@@ -21,7 +21,8 @@ import os, sys, unittest, tempfile
 from timeseriesviewer.utils import initQgisApplication
 from example.Images import Img_2014_06_16_LE72270652014167CUB00_BOA, re_2014_06_25
 qapp = initQgisApplication()
-
+import gdal
+gdal.AllRegister()
 from timeseriesviewer.spectrallibraries import *
 
 class TestInit(unittest.TestCase):
@@ -35,6 +36,31 @@ class TestInit(unittest.TestCase):
         self.layers = [self.lyr1, self.lyr2]
         QgsProject.instance().addMapLayers(self.layers)
 
+
+    def createSpeclib(self):
+        from example.Images import Img_2014_06_16_LE72270652014167CUB00_BOA, re_2014_06_25
+
+        path = Img_2014_06_16_LE72270652014167CUB00_BOA
+        ext = SpatialExtent.fromRasterSource(path)
+        pos = []
+        center = ext.spatialCenter()
+        for dx in range(-120, 120, 60):
+            for dy in range(-120, 120, 60):
+                pos.append(SpatialPoint(ext.crs(), center.x() + dx, center.y() + dy))
+
+        speclib = SpectralLibrary()
+        p1 = SpectralProfile()
+        p1.setName('No Geometry')
+        p1.setXValues([1, 2, 3, 4, 5])
+        p1.setYValues([0.2, 0.3, 0.2, 0.5, 0.7])
+
+        p2 = SpectralProfile()
+        p2.setName('No Geom/NoData')
+
+        speclib.addProfiles([p1,p2])
+        speclib.addSpeclib(SpectralLibrary.readFromRasterPositions(path, pos))
+        return speclib
+
     def test_spectralprofile(self):
 
         canvas = QgsMapCanvas()
@@ -48,6 +74,10 @@ class TestInit(unittest.TestCase):
         for p in profiles:
             self.assertIsInstance(p, SpectralProfile)
 
+
+
+
+
         sp1 = SpectralProfile()
         yVal = [0.23, 0.4, 0.3, 0.8, 0.7]
         xVal = [300,400, 600, 1200, 2500]
@@ -162,6 +192,23 @@ class TestInit(unittest.TestCase):
 
         #read from image
 
+        if self.lyr1.isValid():
+            center1 = self.lyr1.extent().center()
+            center2 = SpatialPoint.fromSpatialExtent(SpatialExtent.fromLayer(self.lyr1))
+        else:
+            center1 = SpatialExtent.fromRasterSource(self.lyr1.source()).spatialCenter()
+            center2 = SpatialExtent.fromRasterSource(self.lyr1.source()).spatialCenter()
+            s  =""
+        sl1 = SpectralLibrary.readFromRasterPositions(Img_2014_06_16_LE72270652014167CUB00_BOA,center1)
+        sl2 = SpectralLibrary.readFromRasterPositions(Img_2014_06_16_LE72270652014167CUB00_BOA,center2)
+
+        sl3 = SpectralLibrary.readFromRasterPositions(Img_2014_06_16_LE72270652014167CUB00_BOA,[center1, center2])
+
+        for sl in [sl1, sl2]:
+            self.assertIsInstance(sl, SpectralLibrary)
+            self.assertTrue(len(sl) == 1)
+        self.assertTrue(len(sl3) == 2)
+
         #sl1.plot()
 
         tempDir = tempfile.gettempdir()
@@ -200,6 +247,51 @@ class TestInit(unittest.TestCase):
 
         self.SPECLIB = sl1
 
+
+
+    def test_mergeSpeclibs(self):
+
+        sp = SpectralProfile()
+        sp.setMetadata('newfield', 'foo', addMissingFields=True)
+        sl1 = SpectralLibrary()
+        sl1.startEditing()
+        sl1.addAttribute(SpectralProfile.createQgsField('newField', ''))
+        sl1.commitChanges()
+        sl1.addProfiles(sp)
+
+        sl2 = SpectralLibrary()
+
+
+    def test_filterModel(self):
+        w = QFrame()
+        speclib = self.createSpeclib()
+        speclib
+        dmodel = SpectralLibraryTableModel(speclib, parent=w)
+        fmodel = SpectralLibraryTableFilterModel(dmodel, parent=w)
+
+        import example
+        layer = QgsVectorLayer(example.exampleEvents)
+        QgsProject.instance().addMapLayer(layer)
+        cache = QgsVectorLayerCache(layer, 1000)
+        table = QgsAttributeTableModel(cache)
+
+
+        dummyCanvas = QgsMapCanvas()
+        dummyCanvas.setDestinationCrs(SpectralProfile.crs)
+        dummyCanvas.setExtent(QgsRectangle(-180,-90,180,90))
+
+        fmodel = QgsAttributeTableFilterModel(dummyCanvas, table)
+        fmodel.sort(0, Qt.DescendingOrder)
+
+        idx0 = fmodel.createIndex(0,0)
+        idx0d = fmodel.mapToSource(idx0)
+        s = ""
+
+    def test_speclibTableView(self):
+
+        v = SpectralLibraryTableView()
+        v.show()
+
     def test_speclibWidget(self):
         p = SpectralLibraryWidget()
         p.addSpeclib(self.SPECLIB)
diff --git a/timeseriesviewer/plotstyling.py b/timeseriesviewer/plotstyling.py
index 2f01f018f96a146a1cb71d38673e3212d86c4959..37f7ffb84ae2e612d6f124a54e75cf2b8c59e80e 100644
--- a/timeseriesviewer/plotstyling.py
+++ b/timeseriesviewer/plotstyling.py
@@ -361,7 +361,7 @@ class PlotStyleDialog(QgsDialog):
         Opens a CrosshairDialog.
         :param args:
         :param kwds:
-        :return: specified CrosshairStyle if accepted, else None
+        :return: specified PlotStyle if accepted, else None
         """
         d = PlotStyleDialog(*args, **kwds)
         d.exec_()
diff --git a/timeseriesviewer/spectrallibraries.py b/timeseriesviewer/spectrallibraries.py
index b2a26b3a8ab9131832fd23fb9414d14edf4b5011..57c0c3c6b98b562646d68558780b1cee8dd9ab34 100644
--- a/timeseriesviewer/spectrallibraries.py
+++ b/timeseriesviewer/spectrallibraries.py
@@ -35,11 +35,13 @@ from osgeo import gdal, gdal_array
 from timeseriesviewer.utils import *
 from timeseriesviewer.virtualrasters import *
 from timeseriesviewer.models import *
+from timeseriesviewer.plotstyling import PlotStyle, PlotStyleDialog
 import timeseriesviewer.mimedata as mimedata
 
 FILTERS = 'ENVI Spectral Library (*.esl *.sli);;CSV Table (*.csv)'
 
-
+PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
+HIDDEN_ATTRIBUTE_PREFIX = '__serialized__'
 
 def gdalDataset(pathOrDataset, eAccess=gdal.GA_ReadOnly):
     """
@@ -83,11 +85,24 @@ def value2str(value, sep=' '):
         value = str(value)
     return value
 
-class SpectralLibraryTableView(QTableView):
+class SpectralLibraryTableFilterModel(QgsAttributeTableFilterModel):
+
+    def __init__(self, sourceModel, parent=None):
+
+        dummyCanvas = QgsMapCanvas()
+        dummyCanvas.setDestinationCrs(SpectralProfile.crs)
+        dummyCanvas.setExtent(QgsRectangle(-180,-90,180,90))
+
+        super(SpectralLibraryTableFilterModel, self).__init__(dummyCanvas, sourceModel, parent=parent)
+
+        self.mDummyCanvas = dummyCanvas
+
+        #self.setSelectedOnTop(True)
+
+class SpectralLibraryTableView(QgsAttributeTableView):
 
     def __init__(self, parent=None):
         super(SpectralLibraryTableView, self).__init__(parent)
-        s = ""
 
     def contextMenuEvent(self, event):
 
@@ -109,8 +124,8 @@ class SpectralLibraryTableView(QTableView):
         a = menu.addAction('Save to file')
         a.triggered.connect(self.onSaveToFile)
 
-        a = menu.addAction('Set color')
-        a.triggered.connect(self.onSetColor)
+        a = menu.addAction('Set Style')
+        a.triggered.connect(self.onSetStyle)
 
         m.addSeparator()
 
@@ -168,25 +183,26 @@ class SpectralLibraryTableView(QTableView):
         m = self.model()
         return [m.idx2profile(m.createIndex(r, 0)) for r in rows]
 
-    def onSetColor(self):
-        model = self.model()
-        c0 = None
-        for r in self.selectedRowsIndexes():
-            idx = model.createIndex(r, 1)
-            c0 = model.data(idx, Qt.BackgroundRole)
-            break
+    def onSetStyle(self):
+        fmodel = self.model()
+        dmodel = self.model()
+        styleDefault = None
 
-        if not isinstance(c0, QColor):
-            c0 = None
+        indices = self.selectionModel().selectedIndexes()
+        if len(indices) == 0:
+            indices.append(self.selectionModel().currentIndex())
 
-        c = QColorDialog(c0, self)
-        c.exec_()
+        if len(indices) > 0:
+            indices = [fmodel.createIndex(idx.row(), 0) for idx in indices]
+            styleDefault = model.data(fmodel.mapToSource(indices[0]), role=Qt.DecorationRole)
 
-        if c.result() == QDialog.Accepted:
-            c = c.currentColor()
-            if isinstance(c, QColor):
-                for r in self.selectedRowsIndexes():
-                    model.setData(model.createIndex(r, 1), c, Qt.BackgroundRole)
+            if not isinstance(styleDefault, PlotStyle):
+                styleDefault = None
+
+            style = PlotStyleDialog.getPlotStyle(plotStyle=styleDefault)
+            if isinstance(style, PlotStyle):
+                for idx in indices:
+                    dmodel.setData(idx, style, Qt.DecorationRole)
 
     def setCheckState(self, checkState):
         model = self.model()
@@ -198,10 +214,6 @@ class SpectralLibraryTableView(QTableView):
         assert isinstance(selectionModel, QItemSelectionModel)
         selectionModel.clearSelection()
 
-    def selectedRowsIndexes(self):
-        selectionModel = self.selectionModel()
-        assert isinstance(selectionModel, QItemSelectionModel)
-        return sorted(list(set([i.row() for i in self.selectionModel().selectedIndexes()])))
 
     def dropEvent(self, event):
         assert isinstance(event, QDropEvent)
@@ -421,9 +433,9 @@ class SpectralProfile(QgsFeature):
         if isinstance(exampleValue, str):
             return QgsField(name, QVariant.String, 'varchar')
         elif isinstance(exampleValue, int):
-            return QgsField(name, QVariant.String, 'int')
+            return QgsField(name, QVariant.Int, 'int')
         elif isinstance(exampleValue, float):
-            return QgsField(name, QVariant.String, 'float')
+            return QgsField(name, QVariant.Double, 'double')
         elif isinstance(exampleValue, np.ndarray):
             return QgsField(name, QVariant.String, 'varchar')
         else:
@@ -448,9 +460,9 @@ class SpectralProfile(QgsFeature):
         fields.append(SpectralProfile.createQgsField('x_unit', ''))
         fields.append(SpectralProfile.createQgsField('y_unit', ''))
         fields.append(SpectralProfile.createQgsField('source', ''))
-        fields.append(SpectralProfile.createQgsField('__serialized__xvalues', ''))
-        fields.append(SpectralProfile.createQgsField('__serialized__yvalues', ''))
-
+        fields.append(SpectralProfile.createQgsField(HIDDEN_ATTRIBUTE_PREFIX + 'xvalues', ''))
+        fields.append(SpectralProfile.createQgsField(HIDDEN_ATTRIBUTE_PREFIX + 'yvalues', ''))
+        fields.append(SpectralProfile.createQgsField(HIDDEN_ATTRIBUTE_PREFIX + 'style', ''))
 
 
         """
@@ -467,9 +479,14 @@ class SpectralProfile(QgsFeature):
     def fromSpecLibFeature(feature):
 
         sp = SpectralProfile(fields=feature.fields())
+        sp.setId(feature.id())
         sp.setAttributes(feature.attributes())
         return sp
 
+    XVALUES_FIELD = HIDDEN_ATTRIBUTE_PREFIX+'xvalues'
+    YVALUES_FIELD = HIDDEN_ATTRIBUTE_PREFIX + 'yvalues'
+    STYLE_FIELD = HIDDEN_ATTRIBUTE_PREFIX + 'style'
+
     def __init__(self, parent=None, fields=None, xUnit='index', yUnit=None):
 
         if fields is None:
@@ -484,6 +501,7 @@ class SpectralProfile(QgsFeature):
 
         self.setXUnit(xUnit)
         self.setYUnit(yUnit)
+        self.setStyle(PlotStyle())
 
     def fieldNames(self):
         return self.fields().names()
@@ -529,7 +547,7 @@ class SpectralProfile(QgsFeature):
             values = values.tolist()
         assert isinstance(values, list)
 
-        self.setMetadata('__serialized__xvalues', values)
+        self.setMetadata(SpectralProfile.XVALUES_FIELD, values)
 
         if isinstance(unit, str):
             self.setMetadata('x_unit', unit)
@@ -538,13 +556,20 @@ class SpectralProfile(QgsFeature):
         if isinstance(values, np.ndarray):
             values = values.tolist()
         assert isinstance(values, list)
-        self.setMetadata('__serialized__yvalues', values)
+        self.setMetadata(SpectralProfile.YVALUES_FIELD, values)
         if isinstance(unit, str):
             self.setMetadata('y_unit', unit)
 
         if self.xValues() is None:
             self.setXValues(list(range(len(values))), unit='index')
 
+    def style(self):
+        return self.metadata(SpectralProfile.STYLE_FIELD)
+
+    def setStyle(self, style):
+        assert isinstance(style, PlotStyle)
+        self.setMetadata(SpectralProfile.STYLE_FIELD, style)
+
     def updateMetadata(self, metaData):
         if isinstance(metaData, dict):
             for key, value in metaData.items():
@@ -608,10 +633,11 @@ class SpectralProfile(QgsFeature):
         return default if v is None else v
 
     def xValues(self):
-        return self.metadata('__serialized__xvalues')
+        return self.metadata(SpectralProfile.XVALUES_FIELD)
+
 
     def yValues(self):
-        return self.metadata('__serialized__yvalues')
+        return self.metadata(SpectralProfile.YVALUES_FIELD)
 
     def setXUnit(self, unit : str='index'):
         self.setMetadata('x_unit', unit)
@@ -694,15 +720,9 @@ class SpectralProfile(QgsFeature):
     def __len__(self):
         return len(self.mValues)
 
-class SpectralLibraryWriter(object):
-
-    @staticmethod
-    def writeSpeclib(speclib):
-        assert isinstance(speclib, SpectralLibrary)
-
 
 
-class SpectralLibraryIO(object):
+class AbstractSpectralLibraryIO(object):
     """
     Abstract Class to define I/O operations for spectral libraries
     Overwrite the canRead and read From routines.
@@ -727,12 +747,12 @@ class SpectralLibraryIO(object):
 
     @staticmethod
     def write(speclib, path):
-        """Writes the SpectralLibrary speclib to path, returns a list of written files"""
+        """Writes the SpectralLibrary to path, returns a list of written files"""
         assert isinstance(speclib, SpectralLibrary)
         return None
 
 
-class CSVSpectralLibraryIO(SpectralLibraryIO):
+class CSVSpectralLibraryIO(AbstractSpectralLibraryIO):
 
     @staticmethod
     def write(speclib, path, separator='\t'):
@@ -777,7 +797,7 @@ class CSVSpectralLibraryIO(SpectralLibraryIO):
         return lines
 
 
-class EnviSpectralLibraryIO(SpectralLibraryIO):
+class EnviSpectralLibraryIO(AbstractSpectralLibraryIO):
 
     @staticmethod
     def canRead(pathESL):
@@ -949,7 +969,7 @@ class EnviSpectralLibraryIO(SpectralLibraryIO):
             else:
                 pathDst = os.path.join(dn, '{}.{}.{}'.format(bn, iGrp, ext))
 
-            drv = gdal.GetDriverByName(str('ENVI'))
+            drv = gdal.GetDriverByName('ENVI')
             assert isinstance(drv, gdal.Driver)
 
 
@@ -1126,92 +1146,8 @@ class SpectralLibraryPanel(QgsDockWidget):
     def setAddCurrentSpectraToSpeclibMode(self, b: bool):
         self.SLW.setAddCurrentSpectraToSpeclibMode(b)
 
-class SpectralLibraryVectorLayer(QgsVectorLayer):
-
-    def __init__(self, speclib, crs=None):
-        assert isinstance(speclib, SpectralLibrary)
-        if crs is None:
-            crs = QgsCoordinateReferenceSystem('EPSG:4326')
-
-        uri = 'Point?crs={}'.format(crs.authid())
-        super(SpectralLibraryVectorLayer, self).__init__(uri, speclib.name(), 'memory', False)
-        self.mCrs = crs
-        self.mSpeclib = speclib
-        self.mSpeclib.sigNameChanged.connect(self.setName)
-        self.nameChanged.connect(self.mSpeclib.setName)
-
-        #todo QGIS3: use QgsFieldContraint instead self.mOIDs
-        self.mOIDs = dict()
-
-
-        #initialize fields
-        assert self.startEditing()
-        # standard field names, types, etc.
-        fieldDefs = [('oid', QVariant.Int, 'integer'),
-                     ('name', QVariant.String, 'string'),
-                     ('geo_x', QVariant.Double, 'decimal'),
-                     ('geo_y', QVariant.Double, 'decimal'),
-                     ('px_x', QVariant.Int, 'integer'),
-                     ('px_y', QVariant.Int, 'integer'),
-                     ('source', QVariant.String, 'string'),
-                     ]
-        # initialize fields
-        for fieldDef in fieldDefs:
-            field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
-            self.addAttribute(field)
-        self.commitChanges()
-
-        self.mSpeclib.sigProfilesAdded[list].connect(self.onProfilesAdded)
-        self.mSpeclib.sigProfilesRemoved[list].connect(self.onProfilesRemoved)
-        self.onProfilesAdded(self.mSpeclib[:])
-
-    def onProfilesAdded(self, profiles):
-        for p in [p for p in profiles if p.geoCoordinate() is not None]:
-            assert isinstance(p, SpectralProfile)
-            oid = str(id(p))
-            if oid in self.mOIDs.keys():
-                continue
-            geo = p.geoCoordinate().toCrs(self.mCrs)
-            if isinstance(geo, SpatialPoint):
-                geometry = QgsPointXY(geo.x(), geo.y())
-                feature = QgsFeature(self.fields())
-                feature.setGeometry(QgsGeometry(geometry))
-                feature.setAttribute('oid', oid)
-                feature.setAttribute('name', str(p.name()))
-                feature.setAttribute('geo_x', p.geoCoordinate().x())
-                feature.setAttribute('geo_y', p.geoCoordinate().y())
-                feature.setAttribute('source', str(p.source()))
-
-                px = p.pxCoordinate()
-                if isinstance(px, QPoint):
-                    feature.setAttribute('px_x', px.x())
-                    feature.setAttribute('px_y', px.y())
-
-            self.startEditing()
-            assert self.addFeature(feature)
-
-            assert self.commitChanges()
-            self.mOIDs[oid] = feature.id()
-            self.updateExtents()
-
-    def onProfilesRemoved(self, profiles):
-
-        oids = [str(id(p)) for p in profiles]
-        oids = [o for o in oids if o in self.mOIDs.keys()]
-        #fids = [self.mOIDs[o] for o in  oids]
-        self.selectByExpression('"oid" in ({})'.format(','.join(oids)))
-        self.deleteSelectedFeatures()
-
-
-    def spectralLibrary(self):
-        return self.mSpeclib
-
-    def nSpectra(self):
-        return len(self.mSpeclib)
-
-
 class SpectralLibrary(QgsVectorLayer):
-    _pickle_protocol = pickle.HIGHEST_PROTOCOL
+
     @staticmethod
     def readFromPickleDump(data):
         return pickle.loads(data)
@@ -1245,6 +1181,26 @@ class SpectralLibrary(QgsVectorLayer):
                 speclib.addSpeclib(sl)
         return speclib
 
+
+    @staticmethod
+    def readFromRasterPositions(pathRaster, positions):
+        #todo: handle vector file input & geometries
+        if not isinstance(positions, list):
+            positions = [positions]
+        profiles = []
+        source = gdal.Open(pathRaster)
+        i = 0
+        for position in positions:
+            profile = SpectralProfile.fromRasterSource(source, position)
+            if isinstance(profile, SpectralProfile):
+                profile.setName('Spectrum {}'.format(i))
+                profiles.append(profile)
+                i += 1
+
+        sl = SpectralLibrary()
+        sl.addProfiles(profiles)
+        return sl
+
     @staticmethod
     def readFrom(uri):
         """
@@ -1252,7 +1208,7 @@ class SpectralLibrary(QgsVectorLayer):
         :param uri: path or uri of the source from which to read SpectralProfiles and return them in a SpectralLibrary
         :return: SpectralLibrary
         """
-        for cls in SpectralLibraryIO.__subclasses__():
+        for cls in AbstractSpectralLibraryIO.__subclasses__():
             if cls.canRead(uri):
                 return cls.readFrom(uri)
         return None
@@ -1270,17 +1226,50 @@ class SpectralLibrary(QgsVectorLayer):
 
         defaultFields = SpectralProfile.standardFields()
 
+
+
         assert self.startEditing()
         assert self.dataProvider().addAttributes(defaultFields)
         assert self.commitChanges()
+        self.initConditionalStyles()
+
+    def initConditionalStyles(self):
+        styles = self.conditionalStyles()
+        assert isinstance(styles, QgsConditionalLayerStyles)
+
+        for fieldName in self.fieldNames():
+            red = QgsConditionalStyle("@value is NULL")
+            red.setTextColor(QColor('red'))
+            styles.setFieldStyles(fieldName, [red])
+
+        red = QgsConditionalStyle('"__serialized__xvalues" is NULL OR "__serialized__yvalues is NULL" ')
+        red.setBackgroundColor(QColor('red'))
+        styles.setRowStyles([red])
+
+
+    def addMissingFields(self, fields):
+        missingFields = []
+        for field in fields:
+            assert isinstance(field, QgsField)
+            i = self.dataProvider().fieldNameIndex(field.name())
+            if i == -1:
+                missingFields.append(field)
+        if len(missingFields) > 0:
+            self.startEditing()
+            self.dataProvider().addAttributes()
+            # for field in missingFields:
+            #    assert isinstance(field, QgsField)
 
+            self.commitChanges()
+            s = ""
 
-
-    def addSpeclib(self, speclib):
+    def addSpeclib(self, speclib, addMissingFields=True):
         assert isinstance(speclib, SpectralLibrary)
+        if addMissingFields:
+            self.addMissingFields(speclib.fields())
         self.addProfiles([p for p in speclib])
 
-    def addProfiles(self, profiles, index : QModelIndex=QModelIndex()):
+    def addProfiles(self, profiles, index : QModelIndex=QModelIndex(), addMissingFields=False):
 
         if isinstance(profiles, SpectralProfile):
             profiles = [profiles]
@@ -1288,16 +1277,22 @@ class SpectralLibrary(QgsVectorLayer):
             profiles = profiles[:]
 
         assert isinstance(profiles, list)
+        if len(profiles) == 0:
+            return
+
         for p in profiles:
             assert isinstance(p, SpectralProfile)
-        a = self.startEditing()
+
+        if addMissingFields:
+            self.addMissingFields(profiles[0])
+
+        inEditMode = self.isEditable()
+        if not inEditMode:
+            self.startEditing()
         #b, l = self.dataProvider().addFeatures(profiles)
-        b = self.addFeatures(profiles)
-        if b:
-            c = self.commitChanges()
-        else:
-            c = self.rollBack()
-        s = ""
+        self.addFeatures(profiles)
+        if not inEditMode:
+            self.commitChanges()
 
     def profiles(self):
         return self[:]
@@ -1323,7 +1318,7 @@ class SpectralLibrary(QgsVectorLayer):
         return CSVSpectralLibraryIO.asTextLines(self, separator=separator)
 
     def asPickleDump(self):
-        return pickle.dumps(self, SpectralLibrary._pickle_protocol)
+        return pickle.dumps(self, SpectralLibrary.PICKLE_PROTOCOL)
 
     def exportProfiles(self, path=None):
 
@@ -1448,7 +1443,7 @@ class SpectralLibrary(QgsVectorLayer):
         return True
 
 
-class SpectralLibraryTableViewModel(QAbstractTableModel):
+class SpectralLibraryTableModel(QgsAttributeTableModel):
 
     #sigPlotStyleChanged = pyqtSignal(SpectralProfile)
     #sigAttributeRemoved = pyqtSignal(str)
@@ -1461,258 +1456,63 @@ class SpectralLibraryTableViewModel(QAbstractTableModel):
             self.style = QColor('white')
             self.checkState = Qt.Unchecked
 
-    def __init__(self, speclib=None, parent=None):
-        super(SpectralLibraryTableViewModel, self).__init__(parent)
-
-        self.cIndex = '#'
-        self.cStyle = 'Style'
-        self.cName = 'Name'
-        self.cPxX = 'px x'
-        self.cPxY = 'px y'
-        self.cCRS = 'CRS'
-        self.cGeoX = 'x'
-        self.cGeoY = 'y'
-        self.cSrc = 'Source'
+        def id(self):
+            return self.profile.id()
 
+    def __init__(self, speclib=None, parent=None):
 
-        self.mAttributeColumns = []
         if speclib is None:
             speclib = SpectralLibrary()
-        assert isinstance(speclib, SpectralLibrary)
-        self.mSpecLib = speclib
-        self.mSpecLib.sigProfilesAdded[list, list].connect(self.onProfilesAdded)
-        self.mSpecLib.sigProfilesRemoved[list,list].connect(self.onProfilesRemoved)
-        self.mProfileWrappers = list() #contains a wrapper for each SpectralProfile
-        self.mProfileWrapperLUT = {}
-
-        #add existing spectral profiles
-        l = len(self.mSpecLib)
-        if  l > 0:
-            self.onProfilesAdded(self.mSpecLib[:], range(l))
-
-    def onProfilesAdded(self, profiles, indices):
-
-
-        for p, i in zip(profiles, indices):
-            assert self.mSpecLib[i] == p
-            self.beginInsertRows(QModelIndex(), i, i)
-            w = SpectralLibraryTableViewModel.ProfileWrapper(p)
-            self.mProfileWrappers.insert(i, w)
-            self.mProfileWrapperLUT[p]=w
-            self.endInsertRows()
-        s = ""
-
-    def onProfilesRemoved(self, profiles, indices):
-        for i, p in zip(indices, profiles):
-            idx = self.profile2idx(p)
-            assert idx.row() == i
-            self.beginRemoveRows(QModelIndex(), i, i)
-            w = self.mProfileWrapperLUT[p]
-            self.mProfileWrappers.remove(w)
-            self.mProfileWrapperLUT.pop(p)
-            self.endRemoveRows()
-
-
-    def addAttribute(self, name, i = None):
-        assert isinstance(name, str)
-        if name != self.cName and name not in self.mAttributeColumns:
-            if i is None:
-                i = len(self.mAttributeColumns)
-            self.beginInsertColumns(QModelIndex(), i, i)
-            self.mAttributeColumns.insert(i, name)
-            self.endInsertColumns()
-
-    def removeAttribute(self, name):
-        assert isinstance(name, str)
-
-        if name in self.mAttributeColumns:
-            i = self.mAttributeColumns
-            self.beginRemoveColumns(QModelIndex(), i,i)
-            self.mAttributeColumns.remove(name)
-            for s in self.mSpecLib:
-                assert isinstance(s, SpectralProfile)
-                if name in s.mMetadata.keys():
-                    s.mMetadata.pop(name)
-            self.endRemoveColumns()
-
-    def columnNames(self):
-
-        return [self.cIndex, self.cStyle, self.cName] + self.mAttributeColumns + \
-               [self.cPxX, self.cPxY, self.cGeoX, self.cGeoY, self.cCRS, self.cSrc]
-
-
-    def insertProfiles(self, profiles, i=None):
-
-        if isinstance(profiles, SpectralLibrary):
-            profiles = profiles[:]
-
-        if not isinstance(profiles, list):
-            profiles =  [profiles]
-
-        self.mSpecLib.addProfiles(profiles, index=i)
-
-
-
-    def removeProfiles(self, profiles):
-        if isinstance(profiles, SpectralLibrary):
-            profiles = profiles[:]
 
-        profiles = [p for p in profiles if isinstance(p, SpectralProfile) and p in self.mSpecLib]
-        self.mSpecLib.removeProfiles(profiles)
-
-    def headerData(self, col, orientation, role):
-        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
-            return self.columnNames()[col]
-        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
-            return col
-        return None
+        cache = QgsVectorLayerCache(speclib, 1000)
 
-    def setHeaderData(self, col, orientation, newName, role=None):
-        oldName = self.columnNames()[col]
-        assert isinstance(newName, str)
-        if orientation == Qt.Horizontal:
-            if role == Qt.EditRole and oldName in self.mAttributeColumns:
-                self.mSpecLib.renameMetadataAttribute(oldName, newName)
-                self.headerDataChanged.emit()
-                return True
-        return False
+        super(SpectralLibraryTableModel, self).__init__(cache, parent)
+        self.mSpeclib = speclib
+        self.mCache = cache
 
+        assert self.mCache.layer() == self.mSpeclib
 
-    def sort(self, col, order):
-        """Sort table by given column number.
-        """
-        return
-        self.layoutAboutToBeChanged.emit()
-        columnName = self.columnNames()[col]
-        rev = order == Qt.DescendingOrder
-        sortedProfiles = None
-        profiles = self.mProfileWrappers.keys()
-        if columnName == self.cName:
-            sortedProfiles = sorted(profiles, key= lambda p: p.name(), reverse=rev)
-        elif columnName == self.cSrc:
-            sortedProfiles = sorted(profiles, key=lambda p: p.source(), reverse=rev)
-        elif columnName == self.cIndex:
-            sortedProfiles = sorted(profiles, key=lambda p: self.mSpecLib.index(p), reverse=rev)
-        elif columnName in self.mAttributeColumns:
-            sortedProfiles = sorted(profiles, key=lambda p: p.metadata(columnName), reverse=rev)
-        if sortedProfiles is not None:
-            tmp = OrderedDict([(p, self.mProfileWrappers[p]) for p in sortedProfiles])
-            self.mProfileWrappers.clear()
-            self.mProfileWrappers.update(tmp)
-        self.layoutChanged.emit()
-
-    def rowCount(self, parentIdx=None, *args, **kwargs):
-        return len(self.mProfileWrappers)
-
-    def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
-        return len(self.columnNames())
-
-    def profile2idx(self, profile):
-        assert isinstance(profile, SpectralProfile)
-        w = self.mProfileWrapperLUT.get(profile)
-        if not isinstance(w, SpectralLibraryTableViewModel.ProfileWrapper):
-            return None
-        else:
-            return self.createIndex(self.mProfileWrappers.index(w), 0, profile)
+        self.loadLayer()
 
-    def profiles2indices(self, profiles):
-        """
-        Returns a list of model-indices related to a list of profiles
-        :param profiles: [list-of-SpectraProfiles]
-        :return: [list-of-QModelIndices]
-        """
-        for p in profiles:
-            assert isinstance(p, SpectralProfile)
-        indices = [self.profile2idx(p) for p in profiles]
-        indices = [i for i in indices if isinstance(i, QModelIndex)]
-        return  indices
+        self.mcnStyle = speclib.fieldNames().index(HIDDEN_ATTRIBUTE_PREFIX+'style')
 
-    def idx2profileWrapper(self, index):
-        """
-        Returns the profile-wrapper related to a model index
-        :param index: QModelIndex
-        :return: ProfileWrapper
-        """
-        assert isinstance(index, QModelIndex)
-        if not index.isValid():
-            return None
 
-        return self.mProfileWrappers[index.row()]
+    def speclib(self):
+        sl = self.mSpeclib
+        assert isinstance(sl, SpectralLibrary)
+        return sl
 
+    def columnNames(self):
+        return self.speclib().fieldNames()
 
-    def indices2profiles(self, indices):
-        """
-        Returns a list of SpectralProfiles related to a list of QModelIndices
-        :param indices: [list-of-QModelIndeices]
-        :return: [list-of-SpectraProfiles]
-        """
-        profiles = [self.idx2profile(idx) for idx in indices]
-        profiles = [p for p in profiles if isinstance(p, SpectralProfile)]
-        return profiles
+    def feature(self, index):
 
+        id = self.rowToId(index.row())
+        f = self.layer().getFeature(id)
 
-    def idx2profile(self, index):
+        return f
 
-        assert isinstance(index, QModelIndex)
-        if not index.isValid():
-            return None
-        else:
-            return self.mProfileWrappers[index.row()].profile
+    def spectralProfile(self, index):
+        feature = self.feature(index)
+        return SpectralProfile.fromSpecLibFeature(feature)
 
     def data(self, index, role=Qt.DisplayRole):
         if role is None or not index.isValid():
             return None
 
-        columnName = self.columnNames()[index.column()]
-        profileWrapper = self.idx2profileWrapper(index)
-        profile = profileWrapper.profile
-        assert isinstance(profile, SpectralProfile)
-        px = profile.pxCoordinate()
-        geo = profile.geoCoordinate()
-        value = None
+        result = super(SpectralLibraryTableModel,self).data(index, role=role)
 
-        if role == Qt.DisplayRole:
-            if columnName == self.cIndex:
-                #value = self.mSpecLib.index(profile)+1
-                pass
-            elif columnName == self.cName:
-                value = profile.name()
-            elif columnName == self.cSrc:
-                value = profile.source()
-            elif columnName in self.mAttributeColumns:
-                value = profile.metadata(columnName)
-                value = '' if value is None else value
-            if px is not None:
-                if columnName == self.cPxX:
-                    value = profile.pxCoordinate().x()
-                elif columnName == self.cPxY:
-                    value = profile.pxCoordinate().y()
-
-            if geo is not None:
-                if columnName == self.cGeoX:
-                    value = '{:0.10f}'.format(profile.geoCoordinate().x())
-                elif columnName == self.cGeoY:
-                    value = '{:0.10f}'.format(profile.geoCoordinate().y())
-                elif columnName == self.cCRS:
-                    value = profile.geoCoordinate().crs().authid()
-
-
-        if role == Qt.BackgroundRole:
-            if columnName == self.cStyle:
-                return self.mProfileWrapperLUT[profile].style
-
-        if role == Qt.EditRole:
-            if columnName == self.cName:
-                value = profile.name()
-            elif columnName in self.mAttributeColumns:
-                value = profile.metadata(columnName)
-
-        if role == Qt.UserRole:
-            value = profile
-        if role == Qt.CheckStateRole:
-            if columnName == self.cIndex:
-                value = profileWrapper.checkState
-        return value
+        if index.column() == 0 and role in [Qt.CheckStateRole, Qt.DecorationRole]:
+            profile = self.spectralProfile(index)
+            style = profile.style()
+            assert isinstance(style, PlotStyle)
+            if role == Qt.CheckStateRole:
+                result = Qt.Checked if style.isVisible() else Qt.Unchecked
+
+            if role == Qt.DecorationRole:
+                result = style.createIcon(QSize(21,21))
+
+        return result
 
 
     def supportedDragActions(self):
@@ -1725,28 +1525,38 @@ class SpectralLibraryTableViewModel(QAbstractTableModel):
     def setData(self, index, value, role=None):
         if role is None or not index.isValid():
             return False
-        assert isinstance(index, QModelIndex)
-        cName = self.columnNames()[index.column()]
-        profileWrapper = self.idx2profileWrapper(index)
-        profile = profileWrapper.profile
-
-        if role  == Qt.EditRole:
-            if cName == self.cName:
-                profile.setName(value)
-                return True
-            if cName in self.mAttributeColumns:
-                profile.setMetadata(cName, value)
-
-        if role == Qt.CheckStateRole:
-            if cName == self.cIndex:
-                profileWrapper.checkState = value
-                return True
-        if role == Qt.BackgroundRole:
-            if cName == self.cStyle:
-                profileWrapper.style = value
-                return True
 
-        return False
+        speclib = self.layer()
+        assert isinstance(speclib, SpectralLibrary)
+        if index.column() == 0 and role in [Qt.CheckStateRole, Qt.DecorationRole]:
+            profile = self.spectralProfile(index)
+            style = profile.style()
+            b = speclib.isEditable()
+
+            assert isinstance(style, PlotStyle)
+            if role == Qt.CheckStateRole:
+                style.setVisibility(value == Qt.Checked)
+                profile.setStyle(style)
+            if role == Qt.DecorationRole and isinstance(value, PlotStyle):
+                profile.setStyle(value)
+
+            if not b:
+                speclib.startEditing()
+            speclib.updateFeature(profile)
+            if not b:
+                speclib.commitChanges()
+            return True
+
+            #f = self.layer().getFeature(profile.id())
+            #i = f.fieldNameIndex(SpectralProfile.STYLE_FIELD)
+            #self.layer().changeAttributeValue(f.id(), i, value)
+            #result = super().setData(self.index(index.row(), self.mcnStyle), value, role=Qt.EditRole)
+            #if not b:
+            #    self.layer().commitChanges()
+
+        else:
+            return super().setData(index, value, role=role)
+
 
     def supportedDragActions(self):
         return Qt.CopyAction
@@ -1790,14 +1600,16 @@ class SpectralLibraryTableViewModel(QAbstractTableModel):
         return types
 
     def flags(self, index):
+
         if index.isValid():
             columnName = self.columnNames()[index.column()]
-            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsDropEnabled
-            if columnName in [self.cName] + self.mAttributeColumns:  # allow check state
-                flags = flags | Qt.ItemIsEditable | Qt.ItemIsUserCheckable
-            if columnName == self.cIndex:
-                flags = flags | Qt.ItemIsUserCheckable
-            return flags
+            if columnName.startswith(HIDDEN_ATTRIBUTE_PREFIX):
+                return Qt.NoItemFlags
+            else:
+                flags = super(SpectralLibraryTableModel, self).flags(index)
+                if index.column() == 0:
+                    flags = flags | Qt.ItemIsUserCheckable
+                return flags
         return None
 
 
@@ -1861,7 +1673,7 @@ class SpectralLibraryWidget(QFrame, loadUI('spectrallibrarywidget.ui')):
         self.tableViewSpeclib.setAcceptDrops(True)
         self.tableViewSpeclib.setDropIndicatorShown(True)
 
-        self.mModel = SpectralLibraryTableViewModel()
+        self.mModel = SpectralLibraryTableModel()
         self.mSpeclib = self.mModel.mSpecLib
 
         self.mSpeclib.sigProfilesAdded.connect(self.onProfilesAdded)
@@ -2098,7 +1910,7 @@ class SpectralLibraryWidget(QFrame, loadUI('spectrallibrarywidget.ui')):
 
 
     def onSelectionChanged(self, selected, deselected):
-        if not isinstance(self.mModel, SpectralLibraryTableViewModel):
+        if not isinstance(self.mModel, SpectralLibraryTableModel):
             return None
 
         assert isinstance(selected, QItemSelection)
@@ -2142,60 +1954,52 @@ if __name__ == "__main__":
     app = initQgisApplication()
     app.messageLog().messageReceived.connect(lambda args: print(args) )
 
+    from example.Images import Img_2014_06_16_LE72270652014167CUB00_BOA, re_2014_06_25
 
-    sp1 = SpectralProfile()
-    sp1.setCoordinates(px=QPoint(10, 20), pt=SpatialPoint(QgsCoordinateReferenceSystem('EPSG:32641'), 30.000, 57.000))
-    sp1.setMetadata('attr1', 24)
-    sp1.setMetadata('attr2', 'foo')
+    p = Img_2014_06_16_LE72270652014167CUB00_BOA
+    ext = SpatialExtent.fromRasterSource(p)
+    pos = []
+    center = ext.spatialCenter()
+    for dx in range(-120,120, 60):
+        for dy in range(-120,120,60):
+            pos.append(SpatialPoint(ext.crs(), center.x()+dx, center.y()+dy))
 
-    sp1b = sp1.clone()
+    speclib = SpectralLibrary()
+    p1 = SpectralProfile()
+    p1.setName('No Geometry')
+    p1.setXValues([1,2,3,4,5])
+    p1.setYValues([0.2,0.3,0.2,0.5,0.7])
 
-    from example.Images import Img_2014_08_11_LC82270652014223LGN00_BOA
-
-
-    sp2 = SpectralProfile()
-    sp2.setCoordinates(px=QPoint(10, 30), pt=SpatialPoint(QgsCoordinateReferenceSystem('EPSG:32641'), 30.000, 57.500))
-    sp2.setMetadata('attr1', 42, addMissingFields=True)
-    sp2.setMetadata('attr2', 'bar', addMissingFields=True)
-
-    sp3 = SpectralProfile()
-    sp3.setCoordinates(px=QPoint(10, 10), pt=SpatialPoint(QgsCoordinateReferenceSystem('EPSG:32641'), 30.000, 57.600))
-    sp3.setMetadata('attr1', 'None')
-    sp3.setMetadata('attr2', 23)
-
-    sl = SpectralLibrary()
-    sl.addProfiles(sp1)
-    sl.addProfiles(sp2)
-    sl.addProfiles(sp3)
+    p2 = SpectralProfile()
+    p2.setName('No Geom/NoData')
 
+    speclib.addProfiles([p1,p2],0)
+    speclib.addSpeclib(SpectralLibrary.readFromRasterPositions(p, pos))
+    speclib.startEditing()
     w = QFrame()
     w.setLayout(QHBoxLayout())
 
-    cache = QgsVectorLayerCache(sl, 1000)
+    model = SpectralLibraryTableModel(speclib=speclib, parent=w)
+    fmodel = SpectralLibraryTableFilterModel(model)
+    view = SpectralLibraryTableView(parent=w)
+    #view = QTableView()
+    view.setModel(fmodel)
 
+    config = QgsAttributeTableConfig()
+    config.update(speclib.fields())
 
-    dmodel = QgsAttributeTableModel(cache)
-    dmodel.loadLayer()
-    dmodel.updatedFields()
+    for i, columnConfig in enumerate(config.columns()):
 
-    dummycanvas = QgsMapCanvas()
-    dummycanvas.setDestinationCrs(SpectralProfile.crs)
-    dummycanvas.setCenter(QgsPointXY(0,0))
-    dummycanvas.setExtent(QgsRectangle(-180, -90,180, 90))
+        if columnConfig.name.startswith(HIDDEN_ATTRIBUTE_PREFIX):
+            config.setColumnHidden(i, True)
 
-    fmodel = QgsAttributeTableFilterModel(dummycanvas, dmodel)
-    fmodel.setSourceModel(dmodel)
-
-    if False:
-        view = QgsAttributeTableView()
-        view.setModel(fmodel)
-    else:
-        view = QTableView()
-        view.setModel(dmodel)
+    speclib.setAttributeTableConfig(config)
+    fmodel.setAttributeTableConfig(config)
+    view.setAttributeTableConfig(config)
 
     w.layout().addWidget(view)
     w.show()
-    w.resize(QSize(300,200))
+    w.resize(QSize(800,200))
 
     if False:
 
diff --git a/timeseriesviewer/utils.py b/timeseriesviewer/utils.py
index 22160458a868b5b0cba83e4ca4461a12043b9ce3..47ed3105d08ac4805ab367870b50426a09267e85 100644
--- a/timeseriesviewer/utils.py
+++ b/timeseriesviewer/utils.py
@@ -1052,6 +1052,8 @@ def initQgisApplication(pythonPlugins=None, PATH_QGIS=None, qgisDebug=False, qgi
         qgsApp = QgsApplication([], True)
         qgsApp.setPrefixPath(PATH_QGIS, True)
         qgsApp.initQgis()
+        qgsApp.registerOgrDrivers()
+
         from qgis.gui import QgsGui
         QgsGui.editorWidgetRegistry().initEditors()