Skip to content
Snippets Groups Projects
profilevisualization.py 79.2 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, PlotStyleDialog
    
    from .externals import pyqtgraph as pg
    from .sensorvisualization import SensorListModel
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from .temporalprofiles import *
    
    from .temporalprofiles3d import *
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from .pixelloader import PixelLoaderTask, doLoaderTask
    
    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 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'))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            # initialize fields
            for fieldDef in fieldDefs:
                field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
                self.addAttribute(field)
            self.commitChanges()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def sensor(self):
            return self.mSensor
    
        def nPixels(self):
            raise NotImplementedError()
    
        def dates(self):
            raise NotImplementedError()
    
    class PlotSettingsModel3D(QAbstractTableModel):
    
        #sigSensorAdded = pyqtSignal(SensorPlotSettings)
        sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
        sigPlotStylesAdded = pyqtSignal(list)
        sigPlotStylesRemoved = pyqtSignal(list)
    
        def __init__(self, parent=None, *args):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            #assert isinstance(tableView, QTableView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            super(PlotSettingsModel3D, self).__init__(parent=parent)
            self.mTimeSeries = None
            self.cnID = 'ID'
            self.cnExpression = LABEL_EXPRESSION_3D
            self.cnTemporalProfile = 'Coordinate'
            self.cnStyle = 'Style'
            self.cnSensor = 'Sensor'
            self.columnNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]
            self.mPlotSettings = []
            #assert isinstance(plotWidget, DateTimePlotWidget)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            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])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __len__(self):
            return len(self.mPlotSettings)
    
        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
    
        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)
    
        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()]
    
        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()
    
        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
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class PlotSettingsModel2D(QAbstractTableModel):
    
        # sigSensorAdded = pyqtSignal(SensorPlotSettings)
    
        sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    
        sigDataChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    
        sigPlotStylesAdded = pyqtSignal(list)
        sigPlotStylesRemoved = pyqtSignal(list)
    
    
        def __init__(self, parent=None, *args):
    
    
    
            super(PlotSettingsModel2D, self).__init__(parent=parent)
    
    
            self.cnID = 'ID'
            self.cnSensor = 'Sensor'
    
            self.cnExpression = LABEL_EXPRESSION_2D
            self.cnStyle = 'Style'
            self.cnTemporalProfile = 'Coordinate'
    
            self.columnNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]
    
            self.mPlotSettings = []
    
            self.dataChanged.connect(self.signaler)
    
        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 testSlot(self, *args):
            print(('TESTSLOT', args))
    
    
        def columnIndex(self, name):
    
            return self.columnNames.index(name)
    
        def signaler(self, idxUL, idxLR):
            if idxUL.isValid():
    
                plotStyle = self.idx2plotStyle(idxUL)
                cname = self.columnNames[idxUL.column()]
                if cname in [self.cnSensor,self.cnStyle]:
                    self.sigVisibilityChanged.emit(plotStyle)
                if cname in [self.cnExpression]:
                    self.sigDataChanged.emit(plotStyle)
    
    
    
    
    
        def requiredBandsIndices(self, sensor)->list:
    
            """
            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)
            for p in [p for p in self.mPlotSettings if p.sensor() == sensor]:
                assert isinstance(p, TemporalProfile2DPlotStyle)
                expression = p.expression()
                #remove leading & tailing "
                bandKeys = regBandKey.findall(expression)
                for bandIndex in [bandKey2bandIndex(key) for key in bandKeys]:
                    bandIndices.add(bandIndex)
    
            return bandIndices
    
    
    
        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):
    
                plotStyles = [plotStyles]
            assert isinstance(plotStyles, list)
            for plotStyle in plotStyles:
    
                assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
    
    
            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 = ""
    
    
    
        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 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, TemporalProfile2DPlotStyle)
    
    
            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, TemporalProfile2DPlotStyle):
    
                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):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                            value = '{} "{}"'.format(tp.id(), tp.name())
    
                        else:
                            value = 'undefined'
    
    
                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.cnExpression:
                        value = plotStyle.expression()
    
                    elif columnName == self.cnStyle:
                        value = plotStyle
    
                    elif columnName == self.cnTemporalProfile:
                        value == plotStyle.temporalProfile()
    
                    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()]
    
    
    
            result = False
            plotStyle = self.idx2plotStyle(index)
    
            if isinstance(plotStyle, TemporalProfile2DPlotStyle):
    
                if role in [Qt.DisplayRole]:
    
                    if columnName == self.cnExpression:
    
                        plotStyle.setExpression(value)
    
                        plotStyle.updateDataProperties()
    
                        result = True
                    elif columnName == self.cnStyle:
                        if isinstance(value, PlotStyle):
                            plotStyle.copyFrom(value)
    
                            plotStyle.updateStyleProperties()
    
                            result = True
    
                if role == Qt.CheckStateRole:
    
                    if columnName == self.cnTemporalProfile:
    
                        plotStyle.setVisibility(value == Qt.Checked)
                        result = True
    
                if role == Qt.EditRole:
    
                    if columnName == self.cnExpression:
    
                        plotStyle.setExpression(value)
    
    
                        result = True
                    elif columnName == self.cnStyle:
                        plotStyle.copyFrom(value)
                        result = True
                    elif columnName == self.cnSensor:
                        plotStyle.setSensor(value)
    
                        result = True
    
                    elif columnName == self.cnTemporalProfile:
                        plotStyle.setTemporalProfile(value)
    
                        result = True
    
            if result:
                #save plot-style
                self.savePlotSettings(plotStyle, index='DEFAULT')
                self.dataChanged.emit(index, index)
    
        def savePlotSettings(self, sensorPlotSettings, index='DEFAULT'):
            return
    
        def restorePlotSettings(self, sensor, index='DEFAULT'):
            return None
    
    
    
        def flags(self, index):
            if index.isValid():
    
                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
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, *args, **kwds):
            super(PlotSettingsTableView, self).__init__(*args, **kwds)
    
            pal = self.palette()
            cSelected = pal.color(QPalette.Active, QPalette.Highlight)
            pal.setColor(QPalette.Inactive, QPalette.Highlight, cSelected)
            self.setPalette(pal)
    
    
        def contextMenuEvent(self, event: QContextMenuEvent):
            """
            Creates and shows the QMenu
            :param event: QContextMenuEvent
            """
    
            indices = self.selectionModel().selectedIndexes()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            indices2 = [self.model().mapToSource(idx) for idx in indices]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if len(indices2) > 0:
                menu = QMenu(self)
                a = menu.addAction('Set Style')
                a.triggered.connect(lambda *args, i=indices2: self.onSetStyle(i))
    
                model = self.model().sourceModel()
                assert isinstance(model, PlotSettingsModel2D)
    
                for idx in indices:
                    style = self.model().sourceModel().idx2plotStyle(idx)
                    if isinstance(style, PlotStyle):
                        refStyle = style
                        break
                if isinstance(refStyle, PlotStyle):
                    newStyle = PlotStyleDialog.getPlotStyle(plotStyle=refStyle)
                    if isinstance(newStyle, PlotStyle):
                        for idx in indices:
                            assert isinstance(idx, QModelIndex)
                            idxStyle = model.createIndex(idx.row(), colStyle)
                            model.setData(idxStyle, newStyle, role=Qt.EditRole)
    
    class PlotSettingsModel2DWidgetDelegate(QStyledItemDelegate):
        """
    
        """
        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()
    
        def paint(self, painter: QPainter, option: 'QStyleOptionViewItem', index: QtCore.QModelIndex):
    
            if index.column() == 2:
                style = self.style(index)
    
                h = self.mTableView.verticalHeader().defaultSectionSize()
                w = self.mTableView.horizontalHeader().defaultSectionSize()
                if h > 0 and w > 0:
                    px = style.createPixmap(size=QSize(w, h))
                    label = QLabel()
                    label.setPixmap(px)
                    painter.drawPixmap(option.rect, px)
                    #QApplication.style().drawControl(QStyle.CE_CustomBase, label, painter)
                else:
                    super(PlotSettingsModel2DWidgetDelegate, self).paint(painter, option, index)
            else:
                super(PlotSettingsModel2DWidgetDelegate, self).paint(painter, option, index)
    
        def sortFilterProxyModel(self)->QSortFilterProxyModel:
            return self.mTableView.model()
    
        def plotSettingsModel(self)->PlotSettingsModel2D:
            return self.sortFilterProxyModel().sourceModel()
    
        def setItemDelegates(self, tableView):
            assert isinstance(tableView, QTableView)
            model = self.plotSettingsModel()
    
            assert isinstance(model, PlotSettingsModel2D)
            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 = self.plotSettingsModel()
            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):
            # 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.plotSettingsModel()
            pmodel = self.sortFilterProxyModel()
    
            w = None
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
    
                plotStyle = model.idx2plotStyle(pmodel.mapToSource(index))
    
                if isinstance(plotStyle, TemporalProfile2DPlotStyle):
                    if cname == model.cnExpression:
    
                        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())
    
                        if isinstance(plotStyle.sensor(), SensorInstrument):
                            w.setLayer(self.exampleLyr(plotStyle.sensor()))
    
                    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()))
    
                    elif cname == model.cnSensor:
                        w = QComboBox(parent=parent)
                        m = SensorListModel(self.mTimeSeries)
                        w.setModel(m)
    
                    elif cname == model.cnTemporalProfile:
                        w = QgsFeatureListComboBox(parent=parent)
                        w.setSourceLayer(self.mTemporalProfileLayer)
                        w.setIdentifierField(FN_ID)
                        w.setDisplayExpression('to_string("{}")+\'  \'+"name"'.format(FN_ID))
                        w.setAllowNull(False)
                    else:
                        raise NotImplementedError()
            return w
    
        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):
    
        def style(self, proxyIndex:QModelIndex)->PlotStyle:
            model = self.plotSettingsModel()
            index = self.sortFilterProxyModel().mapToSource(proxyIndex)
            return model.data(index, role=Qt.UserRole)
    
            model = self.plotSettingsModel()
            index = self.sortFilterProxyModel().mapToSource(proxyIndex)
            w = None
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
                cname = self.getColumnName(proxyIndex)
                if cname == model.cnExpression:
                    lastExpr = model.data(index, Qt.DisplayRole)
                    assert isinstance(editor, QgsFieldExpressionWidget)
                    editor.setProperty('lastexpr', lastExpr)
                    editor.setField(lastExpr)
    
                elif cname == model.cnStyle:
                    style = model.data(index, Qt.UserRole)
                    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)
                    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, proxyIndex):
            index = self.sortFilterProxyModel().mapToSource(proxyIndex)
            cname = self.getColumnName(proxyIndex)
            model = self.plotSettingsModel()
    
            if index.isValid() and isinstance(model, PlotSettingsModel2D):
                if cname == model.cnExpression:
                    assert isinstance(w, QgsFieldExpressionWidget)
                    expr = w.asExpression()
                    exprLast = model.data(index, Qt.DisplayRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed