Skip to content
Snippets Groups Projects
profilevisualization.py 75.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -*- coding: utf-8 -*-
    """
    /***************************************************************************
    
                                  -------------------
            begin                : 2017-08-04
            git sha              : $Format:%H$
            copyright            : (C) 2017 by HU-Berlin
            email                : benjamin.jakimow@geo.hu-berlin.de
     ***************************************************************************/
    
    /***************************************************************************
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     ***************************************************************************/
    """
    # noinspection PyPep8Naming
    
    import os, sys, pickle, datetime
    
    from collections import OrderedDict
    
    from qgis.gui import *
    from qgis.core import *
    
    from qgis.PyQt.QtCore import *
    from qgis.PyQt.QtXml import *
    from qgis.PyQt.QtGui import *
    
    from .timeseries import *
    from .utils import SpatialExtent, SpatialPoint, px2geo, loadUI, nextColor
    from .externals.qps.plotstyling.plotstyling import PlotStyle, PlotStyleButton
    from .externals import pyqtgraph as pg
    from .sensorvisualization import SensorListModel
    from .temporalprofiles2d import *
    from .temporalprofiles3d import *
    
    DEBUG = False
    
    OPENGL_AVAILABLE = False
    
    ENABLE_OPENGL = False
    
        OPENGL_AVAILABLE = True
    
        from eotimeseriesviewer.temporalprofiles3d import *
    
        #t = ViewWidget3D()
        #del t
    
    except Exception as ex:
        print('unable to import OpenGL based packages:\n{}'.format(ex))
    
    
    def getTextColorWithContrast(c):
        assert isinstance(c, QColor)
        if c.lightness() < 0.5:
            return QColor('white')
        else:
            return QColor('black')
    
    def selectedModelIndices(tableView):
        assert isinstance(tableView, QTableView)
        result = {}
    
        sm = tableView.selectionModel()
        m = tableView.model()
        if isinstance(sm, QItemSelectionModel) and isinstance(m, QAbstractItemModel):
            for idx in sm.selectedIndexes():
                assert isinstance(idx, QModelIndex)
                if idx.row() not in result.keys():
                    result[idx.row()] = idx
        return result.values()
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    class _SensorPoints(pg.PlotDataItem):
    
        def __init__(self, *args, **kwds):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            super(_SensorPoints, self).__init__(*args, **kwds)
    
            # menu creation is deferred because it is expensive and often
            # the user will never see the menu anyway.
            self.menu = None
    
        def boundingRect(self):
    
            return super(_SensorPoints,self).boundingRect()
    
            super(_SensorPoints, self).paint(p, *args)
    
    
        # On right-click, raise the context menu
        def mouseClickEvent(self, ev):
            if ev.button() == QtCore.Qt.RightButton:
                if self.raiseContextMenu(ev):
                    ev.accept()
    
        def raiseContextMenu(self, ev):
            menu = self.getContextMenus()
    
            # Let the scene add on to the end of our context menu
            # (this is optional)
            menu = self.scene().addParentContextMenus(self, menu, ev)
    
            pos = ev.screenPos()
            menu.popup(QtCore.QPoint(pos.x(), pos.y()))
            return True
    
        # This method will be called when this item's _children_ want to raise
        # a context menu that includes their parents' menus.
        def getContextMenus(self, event=None):
            if self.menu is None:
    
                self.menu.setTitle(self.name + " options..")
    
    
                green = QAction("Turn green", self.menu)
    
                green.triggered.connect(self.setGreen)
                self.menu.addAction(green)
                self.menu.green = green
    
    
                blue = QAction("Turn blue", self.menu)
    
                blue.triggered.connect(self.setBlue)
                self.menu.addAction(blue)
                self.menu.green = blue
    
    
                alpha = QWidgetAction(self.menu)
                alphaSlider = QSlider()
    
                alphaSlider.setOrientation(QtCore.Qt.Horizontal)
                alphaSlider.setMaximum(255)
                alphaSlider.setValue(255)
                alphaSlider.valueChanged.connect(self.setAlpha)
                alpha.setDefaultWidget(alphaSlider)
                self.menu.addAction(alpha)
                self.menu.alpha = alpha
                self.menu.alphaSlider = alphaSlider
            return self.menu
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class PlotSettingsModel2DWidgetDelegate(QStyledItemDelegate):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        """
    
        def __init__(self, tableView, temporalProfileLayer, parent=None):
    
            assert isinstance(tableView, QTableView)
    
            assert isinstance(temporalProfileLayer, TemporalProfileLayer)
    
            super(PlotSettingsModel2DWidgetDelegate, self).__init__(parent=parent)
    
            self._preferedSize = QgsFieldExpressionWidget().sizeHint()
    
            self.mTableView = tableView
    
            self.mTemporalProfileLayer = temporalProfileLayer
            self.mTimeSeries = temporalProfileLayer.timeSeries()
    
    
            self.mSensorLayers = {}
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def setItemDelegates(self, tableView):
            assert isinstance(tableView, QTableView)
            model = tableView.model()
    
    
            assert isinstance(model, PlotSettingsModel2D)
    
            for c in [model.cnSensor, model.cnExpression, model.cnStyle, model.cnTemporalProfile]:
    
                i = model.columnNames.index(c)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                tableView.setItemDelegateForColumn(i, self)
    
    
        def getColumnName(self, index):
            assert index.isValid()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            model = index.model()
    
            assert isinstance(model, PlotSettingsModel2D)
    
            return model.columnNames[index.column()]
    
        """
        def sizeHint(self, options, index):
            s = super(ExpressionDelegate, self).sizeHint(options, index)
            exprString = self.tableView.model().data(index)
            l = QLabel()
            l.setText(exprString)
            x = l.sizeHint().width() + 100
            s = QSize(x, s.height())
            return self._preferedSize
        """
    
        def exampleLyr(self, sensor):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # if isinstance(sensor, SensorInstrument):
    
            if sensor not in self.mSensorLayers.keys():
    
                crs = QgsCoordinateReferenceSystem('EPSG:4862')
                uri = 'Point?crs={}'.format(crs.authid())
    
                lyr = QgsVectorLayer(uri, 'LOCATIONS', 'memory')
    
                f = sensorExampleQgsFeature(sensor)
                assert isinstance(f, QgsFeature)
                assert lyr.startEditing()
                for field in f.fields():
                    lyr.addAttribute(field)
                lyr.addFeature(f)
                lyr.commitChanges()
                self.mSensorLayers[sensor] = lyr
            return self.mSensorLayers[sensor]
    
    
        def createEditor(self, parent, option, index):
            cname = self.getColumnName(index)
    
            model = self.mTableView.model()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            w = None
    
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                plotStyle = model.idx2plotStyle(index)
    
                if isinstance(plotStyle, TemporalProfile2DPlotStyle):
    
                    if cname == model.cnExpression:
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        w = QgsFieldExpressionWidget(parent=parent)
                        w.setExpressionDialogTitle('Values')
    
                        w.setToolTip('Set an expression to specify the image band or calculate a spectral index.')
    
                        w.fieldChanged[str, bool].connect(lambda n, b: self.checkData(index, w, w.expression()))
                        w.setExpression(plotStyle.expression())
                        plotStyle.sigSensorChanged.connect(lambda s: w.setLayer(self.exampleLyr(s)))
                        if isinstance(plotStyle.sensor(), SensorInstrument):
                            w.setLayer(self.exampleLyr(plotStyle.sensor()))
    
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                    elif cname == model.cnStyle:
                        w = PlotStyleButton(parent=parent)
                        w.setPlotStyle(plotStyle)
                        w.setToolTip('Set style.')
    
                        w.sigPlotStyleChanged.connect(lambda: self.checkData(index, w, w.plotStyle()))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                    elif cname == model.cnSensor:
                        w = QComboBox(parent=parent)
    
                        m = SensorListModel(self.mTimeSeries)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        w.setModel(m)
    
                    elif cname == model.cnTemporalProfile:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        w = QgsFeatureListComboBox(parent=parent)
    
                        w.setSourceLayer(self.mTemporalProfileLayer)
    
                        w.setIdentifierField('id')
                        w.setDisplayExpression('to_string("id")+\'  \'+"name"')
                        w.setAllowNull(False)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    else:
                        raise NotImplementedError()
    
        def checkData(self, index, w, value):
    
            assert isinstance(index, QModelIndex)
            model = self.mTableView.model()
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
                plotStyle = model.idx2plotStyle(index)
                assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
                if isinstance(w, QgsFieldExpressionWidget):
    
                    assert value == w.expression()
                    assert w.isExpressionValid(value) == w.isValidExpression()
    
    
                    if w.isValidExpression():
                        self.commitData.emit(w)
                    else:
                        s = ""
                        #print(('Delegate commit failed',w.asExpression()))
                if isinstance(w, PlotStyleButton):
    
                    self.commitData.emit(w)
    
    
        def setEditorData(self, editor, index):
            cname = self.getColumnName(index)
    
            model = self.mTableView.model()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            w = None
    
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                cname = self.getColumnName(index)
                if cname == model.cnExpression:
                    lastExpr = index.model().data(index, Qt.DisplayRole)
                    assert isinstance(editor, QgsFieldExpressionWidget)
                    editor.setProperty('lastexpr', lastExpr)
                    editor.setField(lastExpr)
    
                elif cname == model.cnStyle:
                    style = index.data()
                    assert isinstance(editor, PlotStyleButton)
                    editor.setPlotStyle(style)
    
                elif cname == model.cnSensor:
                    assert isinstance(editor, QComboBox)
                    m = editor.model()
                    assert isinstance(m, SensorListModel)
                    sensor = index.data(role=Qt.UserRole)
                    if isinstance(sensor, SensorInstrument):
                        idx = m.sensor2idx(sensor)
                        editor.setCurrentIndex(idx.row())
                elif cname == model.cnTemporalProfile:
    
                    assert isinstance(editor, QgsFeatureListComboBox)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    value = editor.identifierValue()
                    if value != QVariant():
                        plotStyle = index.data(role=Qt.UserRole)
                        TP = plotStyle.temporalProfile()
                        editor.setIdentifierValue(TP.id())
                    else:
                        s  = ""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                else:
                    raise NotImplementedError()
    
    
        def setModelData(self, w, model, index):
            cname = self.getColumnName(index)
    
            model = self.mTableView.model()
    
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if cname == model.cnExpression:
                    assert isinstance(w, QgsFieldExpressionWidget)
                    expr = w.asExpression()
                    exprLast = model.data(index, Qt.DisplayRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    if w.isValidExpression() and expr != exprLast:
                        model.setData(index, w.asExpression(), Qt.EditRole)
    
                elif cname == model.cnStyle:
    
                    if isinstance(w, PlotStyleButton):
                        model.setData(index, w.plotStyle(), Qt.EditRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                elif cname == model.cnSensor:
                    assert isinstance(w, QComboBox)
                    sensor = w.itemData(w.currentIndex(), role=Qt.UserRole)
    
                    if isinstance(sensor, SensorInstrument):
                        model.setData(index, sensor, Qt.EditRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                elif cname == model.cnTemporalProfile:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    assert isinstance(w, QgsFeatureListComboBox)
    
                    fid = w.identifierValue()
                    if isinstance(fid, int):
                        TP = self.mTemporalProfileLayer.mProfiles.get(fid)
                        model.setData(index, TP, Qt.EditRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                else:
                    raise NotImplementedError()
    
    
    class PlotSettingsModel3DWidgetDelegate(QStyledItemDelegate):
        """
    
        """
    
        def __init__(self, tableView, temporalProfileLayer, parent=None):
    
            assert isinstance(tableView, QTableView)
    
            assert isinstance(temporalProfileLayer, TemporalProfileLayer)
    
            super(PlotSettingsModel3DWidgetDelegate, self).__init__(parent=parent)
            self._preferedSize = QgsFieldExpressionWidget().sizeHint()
            self.mTableView = tableView
    
            self.mTimeSeries = temporalProfileLayer.timeSeries()
            self.mTemporalProfileLayer = temporalProfileLayer
    
            self.mSensorLayers = {}
    
        def setItemDelegates(self, tableView):
            assert isinstance(tableView, QTableView)
            model = tableView.model()
            assert isinstance(model, PlotSettingsModel3D)
    
            for c in [model.cnSensor, model.cnExpression, model.cnStyle, model.cnTemporalProfile]:
    
                i = model.columnNames.index(c)
    
                tableView.setItemDelegateForColumn(i, self)
    
        def getColumnName(self, index):
            assert index.isValid()
            model = index.model()
            assert isinstance(model, PlotSettingsModel3D)
    
            return model.columnNames[index.column()]
    
        """
        def sizeHint(self, options, index):
            s = super(ExpressionDelegate, self).sizeHint(options, index)
            exprString = self.tableView.model().data(index)
            l = QLabel()
            l.setText(exprString)
            x = l.sizeHint().width() + 100
            s = QSize(x, s.height())
            return self._preferedSize
        """
        def createEditor(self, parent, option, index):
            cname = self.getColumnName(index)
            model = self.mTableView.model()
            w = None
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
                plotStyle = model.idx2plotStyle(index)
                if isinstance(plotStyle, TemporalProfile3DPlotStyle):
    
                    if cname == model.cnExpression:
                        w = QgsFieldExpressionWidget(parent=parent)
                        w.setExpression(plotStyle.expression())
    
                        w.setLayer(self.exampleLyr(plotStyle.sensor()))
                        def onSensorAdded(s):
                            w.setLayer(self.exampleLyr(s))
                        #plotStyle.sigSensorChanged.connect(lambda s : w.setLayer(self.exampleLyr(s)))
                        plotStyle.sigSensorChanged.connect(onSensorAdded)
    
                        w.setExpressionDialogTitle('Values')
                        w.setToolTip('Set an expression to specify the image band or calculate a spectral index.')
    
                        w.fieldChanged[str,bool].connect(lambda n, b : self.checkData(index, w, w.expression()))
    
    
                    elif cname == model.cnStyle:
                        w = TemporalProfile3DPlotStyleButton(parent=parent)
                        w.setPlotStyle(plotStyle)
                        w.setToolTip('Set plot style')
    
                        w.sigPlotStyleChanged.connect(lambda: self.checkData(index, w, w.plotStyle()))
    
                    elif cname == model.cnSensor:
                        w = QComboBox(parent=parent)
    
                        m = SensorListModel(self.mTimeSeries)
                        w.setModel(m)
    
    
                    elif cname == model.cnTemporalProfile:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        w = QgsFeatureListComboBox(parent=parent)
    
                        w.setSourceLayer(self.mTemporalProfileLayer)
    
                        w.setIdentifierField('id')
                        w.setDisplayExpression('to_string("id")+\'  \'+"name"')
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        w.setAllowNull(False)
    
                    else:
                        raise NotImplementedError()
    
        def exampleLyr(self, sensor):
    
            if sensor not in self.mSensorLayers.keys():
                crs = QgsCoordinateReferenceSystem('EPSG:4862')
                uri = 'Point?crs={}'.format(crs.authid())
                lyr = QgsVectorLayer(uri, 'LOCATIONS', 'memory')
    
                assert sensor is None or isinstance(sensor, SensorInstrument)
    
    
                f = sensorExampleQgsFeature(sensor, singleBandOnly=True)
                assert isinstance(f, QgsFeature)
                assert lyr.startEditing()
                for field in f.fields():
                    lyr.addAttribute(field)
                lyr.addFeature(f)
                lyr.commitChanges()
                self.mSensorLayers[sensor] = lyr
            return self.mSensorLayers[sensor]
    
    
        def checkData(self, index, w, value):
    
            assert isinstance(index, QModelIndex)
            model = self.mTableView.model()
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
                plotStyle = model.idx2plotStyle(index)
                assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
    
                if isinstance(w, QgsFieldExpressionWidget):
                    assert value == w.expression()
                    assert w.isExpressionValid(value) == w.isValidExpression()
    
                    if w.isValidExpression():
                        self.commitData.emit(w)
                    else:
                        s = ""
                        #print(('Delegate commit failed',w.asExpression()))
    
                if isinstance(w, TemporalProfile3DPlotStyleButton):
    
                    self.commitData.emit(w)
    
    
        def setEditorData(self, editor, index):
            cname = self.getColumnName(index)
            model = self.mTableView.model()
    
            w = None
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
                cname = self.getColumnName(index)
    
                style = model.idx2plotStyle(index)
    
                if cname == model.cnExpression:
                    lastExpr = index.model().data(index, Qt.DisplayRole)
                    assert isinstance(editor, QgsFieldExpressionWidget)
                    editor.setProperty('lastexpr', lastExpr)
                    editor.setField(lastExpr)
    
                elif cname == model.cnStyle:
                    assert isinstance(editor, TemporalProfile3DPlotStyleButton)
                    editor.setPlotStyle(style)
    
                elif cname == model.cnSensor:
                    assert isinstance(editor, QComboBox)
                    m = editor.model()
                    assert isinstance(m, SensorListModel)
                    sensor = index.data(role=Qt.UserRole)
                    if isinstance(sensor, SensorInstrument):
                        idx = m.sensor2idx(sensor)
                        editor.setCurrentIndex(idx.row())
                elif cname == model.cnTemporalProfile:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    assert isinstance(editor, QgsFeatureListComboBox)
    
                    value = editor.identifierValue()
                    if value != QVariant():
                        plotStyle = index.data(role=Qt.UserRole)
                        TP = plotStyle.temporalProfile()
                        editor.setIdentifierValue(TP.id())
                    else:
                        s  = ""
    
                else:
                    raise NotImplementedError()
    
        def setModelData(self, w, model, index):
            cname = self.getColumnName(index)
            model = self.mTableView.model()
    
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
    
                if cname == model.cnExpression:
                    assert isinstance(w, QgsFieldExpressionWidget)
                    expr = w.asExpression()
                    exprLast = model.data(index, Qt.DisplayRole)
    
                    if w.isValidExpression() and expr != exprLast:
                        model.setData(index, w.asExpression(), Qt.EditRole)
    
                elif cname == model.cnStyle:
                    assert isinstance(w, TemporalProfile3DPlotStyleButton)
                    model.setData(index, w.plotStyle(), Qt.EditRole)
    
                elif cname == model.cnSensor:
                    assert isinstance(w, QComboBox)
                    sensor = w.itemData(w.currentIndex(), role=Qt.UserRole)
                    assert isinstance(sensor, SensorInstrument)
                    model.setData(index, sensor, Qt.EditRole)
    
    
                elif cname == model.cnTemporalProfile:
    
                    assert isinstance(w, QgsFeatureListComboBox)
                    fid = w.identifierValue()
                    if isinstance(fid, int):
                        TP = self.mTemporalProfileLayer.mProfiles.get(fid)
                        model.setData(index, TP, Qt.EditRole)
    
                else:
                    raise NotImplementedError()
    
    
    
    class SensorPixelDataMemoryLayer(QgsVectorLayer):
    
        def __init__(self, sensor, crs=None):
            assert isinstance(sensor, SensorInstrument)
            if crs is None:
                crs = QgsCoordinateReferenceSystem('EPSG:4862')
            uri = 'Point?crs={}'.format(crs.authid())
            super(SensorPixelDataMemoryLayer, self).__init__(uri, 'Pixels_sensor_' + sensor.name(), 'memory', False)
            self.mSensor = sensor
    
            #initialize fields
            assert self.startEditing()
            # standard field names, types, etc.
            fieldDefs = [('pxid', QVariant.String, 'integer'),
                         ('date', QVariant.String, 'char'),
                         ('doy', QVariant.Int, 'integer'),
                         ('geo_x', QVariant.Double, 'decimal'),
                         ('geo_y', QVariant.Double, 'decimal'),
                         ('px_x', QVariant.Int, 'integer'),
                         ('px_y', QVariant.Int, 'integer'),
                         ]
            # one field for each band
            for b in range(sensor.nb):
                fName = 'b{}'.format(b + 1)
                fieldDefs.append((fName, QVariant.Double, 'decimal'))
    
            # initialize fields
            for fieldDef in fieldDefs:
                field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
                self.addAttribute(field)
            self.commitChanges()
    
        def sensor(self):
            return self.mSensor
    
        def nPixels(self):
            raise NotImplementedError()
    
        def dates(self):
            raise NotImplementedError()
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class PlotSettingsModel3D(QAbstractTableModel):
    
        #sigSensorAdded = pyqtSignal(SensorPlotSettings)
        sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
        sigPlotStylesAdded = pyqtSignal(list)
        sigPlotStylesRemoved = pyqtSignal(list)
    
        def __init__(self, parent=None, *args):
    
            #assert isinstance(tableView, QTableView)
    
            super(PlotSettingsModel3D, self).__init__(parent=parent)
            self.mTimeSeries = None
    
            self.cnID = 'ID'
            self.cnExpression = LABEL_EXPRESSION_3D
            self.cnTemporalProfile = 'Coordinate'
    
            self.cnSensor = 'Sensor'
    
            self.columnNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]
    
            self.mPlotSettings = []
            #assert isinstance(plotWidget, DateTimePlotWidget)
    
            self.sortColumnIndex = 0
            self.sortOrder = Qt.AscendingOrder
    
    
            self.sort(0, Qt.AscendingOrder)
    
        def hasStyleForSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
            for plotStyle in self.mPlotSettings:
                assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                if plotStyle.sensor() == sensor:
                    return True
            return False
    
    
    
        def onSensorRemoved(self, sensor):
            assert isinstance(sensor, SensorInstrument)
            self.removePlotStyles([s for s in self.mPlotSettings if s.sensor() == sensor])
    
    
    
        def __len__(self):
            return len(self.mPlotSettings)
    
        def __iter__(self):
            return iter(self.mPlotSettings)
    
        def __getitem__(self, slice):
            return self.mPlotSettings[slice]
    
        def __contains__(self, item):
            return item in self.mPlotSettings
    
    
        def columnIndex(self, name):
    
            return self.columnNames.index(name)
    
    
    
        def insertPlotStyles(self, plotStyles, i=None):
            """
            Inserts PlotStyle
            :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
            :param i: index to insert, defaults to the last list position
            """
            if isinstance(plotStyles, TemporalProfile3DPlotStyle):
                plotStyles = [plotStyles]
            assert isinstance(plotStyles, list)
            for plotStyle in plotStyles:
                assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
    
            if i is None:
                i = len(self.mPlotSettings)
    
            if len(plotStyles) > 0:
                self.beginInsertRows(QModelIndex(), i, i + len(plotStyles)-1)
                for j, plotStyle in enumerate(plotStyles):
                    assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                    self.mPlotSettings.insert(i+j, plotStyle)
                self.endInsertRows()
                self.sigPlotStylesAdded.emit(plotStyles)
    
        def removePlotStyles(self, plotStyles):
            """
            Removes PlotStyle instances
            :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
            """
            if isinstance(plotStyles, TemporalProfile3DPlotStyle):
                plotStyles = [plotStyles]
            assert isinstance(plotStyles, list)
    
            if len(plotStyles) > 0:
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                    if plotStyle in self.mPlotSettings:
                        idx = self.plotStyle2idx(plotStyle)
                        self.beginRemoveRows(QModelIndex(), idx.row(),idx.row())
                        self.mPlotSettings.remove(plotStyle)
                        self.endRemoveRows()
                self.sigPlotStylesRemoved.emit(plotStyles)
    
        def sort(self, col, order):
            if self.rowCount() == 0:
                return
    
    
            colName = self.columnames[col]
            r = order != Qt.AscendingOrder
    
            #self.beginMoveRows(idxSrc,
    
            if colName == self.cnSensor:
                self.mPlotSettings.sort(key = lambda sv:sv.sensor().name(), reverse=r)
    
        def rowCount(self, parent = QModelIndex()):
            return len(self.mPlotSettings)
    
    
        def removeRows(self, row, count , parent = QModelIndex()):
    
            self.beginRemoveRows(parent, row, row + count-1)
    
            toRemove = self.mPlotSettings[row:row + count]
    
            for tsd in toRemove:
                self.mPlotSettings.remove(tsd)
    
            self.endRemoveRows()
    
        def plotStyle2idx(self, plotStyle):
    
            assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
    
            if plotStyle in self.mPlotSettings:
                i = self.mPlotSettings.index(plotStyle)
                return self.createIndex(i, 0)
            else:
                return QModelIndex()
    
        def idx2plotStyle(self, index):
    
            if index.isValid() and index.row() < self.rowCount():
                return self.mPlotSettings[index.row()]
    
            return None
    
        def columnCount(self, parent = QModelIndex()):
    
            return len(self.columnNames)
    
    
        def data(self, index, role = Qt.DisplayRole):
            if role is None or not index.isValid():
                return None
    
            value = None
    
            columnName = self.columnNames[index.column()]
    
            plotStyle = self.idx2plotStyle(index)
            if isinstance(plotStyle, TemporalProfile3DPlotStyle):
                sensor = plotStyle.sensor()
                #print(('data', columnName, role))
                if role == Qt.DisplayRole:
                    if columnName == self.cnSensor:
                        if isinstance(sensor, SensorInstrument):
                            value = sensor.name()
                        else:
                            value = '<Select Sensor>'
    
                    elif columnName == self.cnExpression:
                        value = plotStyle.expression()
                    elif columnName == self.cnTemporalProfile:
                        tp = plotStyle.temporalProfile()
                        if isinstance(tp, TemporalProfile):
                            value = tp.name()
                        else:
                            value = 'undefined'
    
                elif role == Qt.EditRole:
                    if columnName == self.cnExpression:
                        value = plotStyle.expression()
    
    
                elif role == Qt.CheckStateRole:
    
                    if columnName == self.cnTemporalProfile:
    
                        value = Qt.Checked if plotStyle.isVisible() else Qt.Unchecked
    
                elif role == Qt.UserRole:
                    value = plotStyle
                    if columnName == self.cnSensor:
                        value = plotStyle.sensor()
                    elif columnName == self.cnStyle:
                        value = plotStyle
                    else:
                        value = plotStyle
            #print(('get data',value))
            return value
    
        def setData(self, index, value, role=None):
            if role is None or not index.isValid():
                return False
            #print(('Set data', index.row(), index.column(), value, role))
    
            columnName = self.columnNames[index.column()]
    
    
            if value is None:
                return False
    
            result = False
            plotStyle = self.idx2plotStyle(index)
            if isinstance(plotStyle, TemporalProfile3DPlotStyle):
    
                if role in [Qt.DisplayRole]:
    
                    if columnName == self.cnExpression and isinstance(value, str):
                        plotStyle.setExpression(value)
    
                        result = True
                    elif columnName == self.cnStyle:
                        if isinstance(value, PlotStyle):
                            plotStyle.copyFrom(value)
                            result = True
    
    
                if role == Qt.CheckStateRole:
    
                    if columnName == self.cnTemporalProfile:
    
                        plotStyle.setVisibility(value == Qt.Checked)
                        result = True
    
                if role == Qt.EditRole:
    
                    if columnName == self.cnSensor:
                        plotStyle.setSensor(value)
                        result = True
                    elif columnName == self.cnExpression:
                        plotStyle.setExpression(value)
    
                        result = True
    
                    elif columnName == self.cnTemporalProfile:
                        plotStyle.setTemporalProfile(value)
    
                        result = True
                    elif columnName == self.cnStyle:
    
                        #set the style and trigger an update
                        lastItemType = plotStyle.itemType()
                        lastExpression = plotStyle.expression()
    
                        plotStyle.copyFrom(value)
    
    
                        if lastItemType != plotStyle.itemType() or \
                            lastExpression != plotStyle.expression():
                            plotStyle.updateDataProperties()
                        else:
                            plotStyle.updateStyleProperties()
    
    
    
            return result
    
    
        def flags(self, index):
            if index.isValid():
    
                stype = self.idx2plotStyle(index)
    
                columnName = self.columnNames[index.column()]
    
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
    
                if columnName in [self.cnTemporalProfile]:
    
                    flags = flags | Qt.ItemIsUserCheckable
    
                if columnName in [self.cnTemporalProfile, self.cnSensor, self.cnExpression, self.cnStyle]: #allow check state
    
                    flags = flags | Qt.ItemIsEditable
                return flags
                #return item.qt_flags(index.column())
            return Qt.NoItemFlags
    
        def headerData(self, col, orientation, role):
            if Qt is None:
                return None
            if orientation == Qt.Horizontal and role == Qt.DisplayRole:
    
                return self.columnNames[col]
    
            elif orientation == Qt.Vertical and role == Qt.DisplayRole:
                return col
            return None
    
    
    class PlotSettingsModel2D(QAbstractTableModel):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        # sigSensorAdded = pyqtSignal(SensorPlotSettings)
    
        sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
        sigDataChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    
        sigPlotStylesAdded = pyqtSignal(list)
        sigPlotStylesRemoved = pyqtSignal(list)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, parent=None, *args):
    
            super(PlotSettingsModel2D, self).__init__(parent=parent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.cnID = 'ID'
    
            self.cnExpression = LABEL_EXPRESSION_2D
    
            self.cnTemporalProfile = 'Coordinate'
    
            self.columnNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.mPlotSettings = []
            #assert isinstance(plotWidget, DateTimePlotWidget)
    
            #self.mPlotWidget = plotWidget
    
            self.sortColumnIndex = 0
            self.sortOrder = Qt.AscendingOrder
    
            #assert isinstance(self.tpCollection.TS, TimeSeries)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.tpCollection.TS.sigSensorAdded.connect(self.addPlotItem)
            #self.tpCollection.TS.sigSensorRemoved.connect(self.removeSensor)
    
    
            self.sort(0, Qt.AscendingOrder)
            self.dataChanged.connect(self.signaler)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __len__(self):
            return len(self.mPlotSettings)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __iter__(self):
            return iter(self.mPlotSettings)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __getitem__(self, slice):
            return self.mPlotSettings[slice]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __contains__(self, item):
            return item in self.mPlotSettings
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def testSlot(self, *args):
            print(('TESTSLOT', args))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def columnIndex(self, name):
    
            return self.columnNames.index(name)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def signaler(self, idxUL, idxLR):
            if idxUL.isValid():
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                plotStyle = self.idx2plotStyle(idxUL)
    
                cname = self.columnNames[idxUL.column()]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if cname in [self.cnSensor,self.cnStyle]:
                    self.sigVisibilityChanged.emit(plotStyle)
                if cname in [self.cnExpression]:
                    self.sigDataChanged.emit(plotStyle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def requiredBandsIndices(self, sensor):
            """
            Returns the band indices required to calculate the values for
            the different PlotStyle expressions making use of sensor
            :param sensor: SensorInstrument for which the band indices are to be returned.
            :return: [list-of-band-indices]
            """
            bandIndices = set()
    
            assert isinstance(sensor, SensorInstrument)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for p in [p for p in self.mPlotSettings if p.sensor() == sensor]:
    
                assert isinstance(p, TemporalProfile2DPlotStyle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                expression = p.expression()
                #remove leading & tailing "
    
                bandKeys = regBandKey.findall(expression)
    
                for bandIndex in [bandKey2bandIndex(key) for key in bandKeys]:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    bandIndices.add(bandIndex)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return bandIndices
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def insertPlotStyles(self, plotStyles, i=None):
            """
            Inserts PlotStyle
            :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
            :param i: index to insert, defaults to the last list position
            """
    
            if isinstance(plotStyles, TemporalProfile2DPlotStyle):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                plotStyles = [plotStyles]
            assert isinstance(plotStyles, list)
            for plotStyle in plotStyles:
    
                assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if i is None:
                i = len(self.mPlotSettings)
    
            if len(plotStyles) > 0:
                self.beginInsertRows(QModelIndex(), i, i + len(plotStyles)-1)
                for j, plotStyle in enumerate(plotStyles):
    
                    assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
    
                    plotStyle.sigExpressionUpdated.connect(lambda s = plotStyle: self.onStyleUpdated(s))
    
                    self.mPlotSettings.insert(i+j, plotStyle)
                self.endInsertRows()
                self.sigPlotStylesAdded.emit(plotStyles)
    
        def onStyleUpdated(self, style):
    
            idx = self.plotStyle2idx(style)
            r = idx.row()
            self.dataChanged.emit(self.createIndex(r, 0), self.createIndex(r, self.columnCount()))
    
            s = ""
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def removePlotStyles(self, plotStyles):
            """
            Removes PlotStyle instances
            :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
            """
            if isinstance(plotStyles, PlotStyle):
                plotStyles = [plotStyles]
            assert isinstance(plotStyles, list)
    
    
            if len(plotStyles) > 0:
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, PlotStyle)
                    if plotStyle in self.mPlotSettings:
                        idx = self.plotStyle2idx(plotStyle)
                        self.beginRemoveRows(QModelIndex(), idx.row(),idx.row())
                        self.mPlotSettings.remove(plotStyle)
                        self.endRemoveRows()
                self.sigPlotStylesRemoved.emit(plotStyles)
    
    
        def sort(self, col, order):
            if self.rowCount() == 0:
                return
    
    
            colName = self.columnames[col]
            r = order != Qt.AscendingOrder
    
            #self.beginMoveRows(idxSrc,
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if colName == self.cnSensor: