Skip to content
Snippets Groups Projects
profilevisualization.py 74.8 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 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):
            """
            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):
                            value = tp.name()
                        else:
                            value = 'undefined'
    
    
                if role == Qt.DecorationRole:
                    if columnName == self.cnStyle:
                        value = plotStyle.createIcon(QSize(25,25))
    
    
                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
    
    
    
    class ProfileViewDockUI(QgsDockWidget, loadUI('profileviewdock.ui')):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, parent=None):
            super(ProfileViewDockUI, self).__init__(parent)
            self.setupUi(self)
    
            self.addActions(self.findChildren(QAction))
    
            self.mActions2D = [self.actionAddStyle2D, self.actionRemoveStyle2D, self.actionRefresh2D, self.actionReset2DPlot]
            self.mActions3D = [self.actionAddStyle3D, self.actionRemoveStyle3D, self.actionRefresh3D,
                               self.actionReset3DCamera]
            self.mActionsTP = [self.actionLoadTPFromOgr, self.actionSaveTemporalProfiles, self.actionToggleEditing,
                               self.actionRemoveTemporalProfile, self.actionLoadMissingValues]
            #TBD.
            #self.line.setVisible(False)
            #self.listWidget.setVisible(False)
            self.baseTitle = self.windowTitle()
            self.stackedWidget.currentChanged.connect(self.onStackPageChanged)
    
            self.plotWidget3D = None
            self.plotWidget3DMPL = None
    
            self.init3DWidgets('gl')
    
            #pi = self.plotWidget2D.plotItem
            #ax = DateAxis(orientation='bottom', showValues=True)
            #pi.layout.addItem(ax, 3,2)
    
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(100)
            self.progressBar.setValue(0)
            self.progressInfo.setText('')
            self.pxViewModel2D = None
            self.pxViewModel3D = None
    
            self.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
    
        def init3DWidgets(self, mode='gl'):
            assert mode in ['gl']
            l = self.frame3DPlot.layout()
    
            if ENABLE_OPENGL and OPENGL_AVAILABLE and mode == 'gl':
    
                from eotimeseriesviewer.temporalprofiles3dGL import ViewWidget3D
                self.plotWidget3D = ViewWidget3D(parent=self.labelDummy3D.parent())
                self.plotWidget3D.setObjectName('plotWidget3D')
    
                size = self.labelDummy3D.size()
                l.addWidget(self.plotWidget3D)
                self.plotWidget3D.setSizePolicy(self.labelDummy3D.sizePolicy())
                self.labelDummy3D.setVisible(False)
                l.removeWidget(self.labelDummy3D)
                #self.plotWidget3D.setBaseSize(size)
                self.splitter3D.setSizes([100, 100])
                self.frameSettings3D.setEnabled(True)
            else:
                self.frameSettings3D.setEnabled(False)
    
        def onStackPageChanged(self, i):
            w = self.stackedWidget.currentWidget()
            title = self.baseTitle
            if w == self.page2D:
                title = '{} | 2D'.format(title)
                for a in self.mActions2D:
                    a.setVisible(True)
                for a in self.mActions3D + self.mActionsTP:
                    a.setVisible(False)
            elif w == self.page3D:
                title = '{} | 3D (experimental!)'.format(title)
                for a in self.mActions2D + self.mActionsTP:
                    a.setVisible(False)
                for a in self.mActions3D:
                    a.setVisible(True)
            elif w == self.pagePixel:
                title = '{} | Coordinates'.format(title)
                for a in self.mActions2D + self.mActions3D:
                    a.setVisible(False)
                for a in self.mActionsTP:
                    a.setVisible(True)
    
            w.update()
            self.setWindowTitle(title)
    
    class SpectralTemporalVisualization(QObject):
    
        sigShowPixel = pyqtSignal(TimeSeriesDatum, QgsPoint, QgsCoordinateReferenceSystem)
    
        """
        Signalizes to move to specific date of interest
        """
        sigMoveToDate = pyqtSignal(np.datetime64)
    
        def __init__(self, timeSeries, profileDock):
            super(SpectralTemporalVisualization, self).__init__()
    
            assert isinstance(profileDock, ProfileViewDockUI)
            self.ui = profileDock
    
            import eotimeseriesviewer.pixelloader
            if DEBUG:
                eotimeseriesviewer.pixelloader.DEBUG = True
    
            #the timeseries. will be set later
            assert isinstance(timeSeries, TimeSeries)
            self.TS = timeSeries
            self.plot_initialized = False
    
            self.plot2D = self.ui.plotWidget2D
            self.plot2D.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
            self.plot3D = self.ui.plotWidget3D
    
            # temporal profile collection to store loaded values
            self.mTemporalProfileLayer = TemporalProfileLayer(self.TS)
            self.mTemporalProfileLayer.sigTemporalProfilesAdded.connect(self.onTemporalProfilesAdded)
            #self.mTemporalProfileLayer.startEditing()
            self.mTemporalProfileLayer.selectionChanged.connect(self.onTemporalProfileSelectionChanged)
    
            # fix to not loose C++ reference on temporal profile layer in case it is removed from QGIS mapcanvas
            self.mMapCanvas = QgsMapCanvas()
            self.mMapCanvas.setVisible(False)
            self.mMapCanvas.setLayers([self.mTemporalProfileLayer])
            # self.tpCollectionListModel = TemporalProfileCollectionListModel(self.tpCollection)
    
            assert isinstance(self.ui.mDualView, QgsDualView)
            self.ui.mDualView.init(self.mTemporalProfileLayer, self.mMapCanvas)
            self.ui.mDualView.setView(QgsDualView.AttributeTable)
            # pixel loader to load pixel values in parallel
            config = QgsAttributeTableConfig()
            config.update(self.mTemporalProfileLayer.fields())
            config.setActionWidgetVisible(True)
    
            hidden = [FN_ID]
            for i, columnConfig in enumerate(config.columns()):
    
                assert isinstance(columnConfig, QgsAttributeTableConfig.ColumnConfig)
                config.setColumnHidden(i, columnConfig.name in hidden)
    
            self.mTemporalProfilesTableConfig = config
            self.mTemporalProfileLayer.setAttributeTableConfig(self.mTemporalProfilesTableConfig)
    
            self.pixelLoader = PixelLoader()
            self.pixelLoader.sigPixelLoaded.connect(self.onPixelLoaded)
            self.pixelLoader.sigLoadingStarted.connect(lambda: self.ui.progressInfo.setText('Start loading...'))
            # self.pixelLoader.sigLoadingStarted.connect(self.tpCollection.prune)
            self.pixelLoader.sigLoadingFinished.connect(lambda: self.plot2D.enableAutoRange('x', False))
    
            # set the plot models for 2D
            self.plotSettingsModel2D = PlotSettingsModel2D()
            self.plotSettingsModel2DProxy = QSortFilterProxyModel()
            self.plotSettingsModel2DProxy.setSourceModel(self.plotSettingsModel2D)
            self.ui.tableView2DProfiles.setModel(self.plotSettingsModel2DProxy)
            self.ui.tableView2DProfiles.setSortingEnabled(True)
            self.ui.tableView2DProfiles.selectionModel().selectionChanged.connect(self.onPlot2DSelectionChanged)
            self.ui.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
            self.plotSettingsModel2D.sigDataChanged.connect(self.requestUpdate)
            self.plotSettingsModel2D.rowsInserted.connect(self.onRowsInserted2D)
            self.delegateTableView2D = PlotSettingsModel2DWidgetDelegate(self.ui.tableView2DProfiles, self.mTemporalProfileLayer)
            self.delegateTableView2D.setItemDelegates(self.ui.tableView2DProfiles)
    
            # set the plot models for 3D
            self.plotSettingsModel3D = PlotSettingsModel3D()
            self.ui.tableView3DProfiles.setModel(self.plotSettingsModel3D)
            self.ui.tableView3DProfiles.setSortingEnabled(False)
            self.ui.tableView2DProfiles.selectionModel().selectionChanged.connect(self.onPlot3DSelectionChanged)
            self.ui.tableView3DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
            self.plotSettingsModel3D.rowsInserted.connect(self.onRowsInserted3D)
            self.delegateTableView3D = PlotSettingsModel3DWidgetDelegate(self.ui.tableView3DProfiles, self.mTemporalProfileLayer)
            self.delegateTableView3D.setItemDelegates(self.ui.tableView3DProfiles)
    
            if not ENABLE_OPENGL:
                self.ui.listWidget.item(1).setHidden(True)
                self.ui.page3D.setHidden(True)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            def onTemporalProfilesRemoved(removedProfiles):
                #set to valid temporal profile
    
                affectedStyles2D = [p for p in self.plotSettingsModel2D if p.temporalProfile() in removedProfiles]
                affectedStyles3D = [p for p in self.plotSettingsModel3D if p.temporalProfile() in removedProfiles]
                alternativeProfile = self.mTemporalProfileLayer[-1] if len(self.mTemporalProfileLayer) > 0 else None
                for s in affectedStyles2D:
                    assert isinstance(s, TemporalProfile2DPlotStyle)
                    m = self.plotSettingsModel2D
                    idx = m.plotStyle2idx(s)
                    idx = m.createIndex(idx.row(), m.columnNames.index(m.cnTemporalProfile))
                    m.setData(idx, alternativeProfile, Qt.EditRole)
    
                for s in affectedStyles3D:
                    assert isinstance(s, TemporalProfile3DPlotStyle)
                    m = self.plotSettingsModel3D
                    idx = m.plotStyle2idx(s)
                    idx = m.createIndex(idx.row(), m.columnNames.index(m.cnTemporalProfile))
                    m.setData(idx, alternativeProfile, Qt.EditRole)
    
            self.mTemporalProfileLayer.sigTemporalProfilesRemoved.connect(onTemporalProfilesRemoved)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            # remove / add plot style
            def on2DPlotStyleRemoved(plotStyles):
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, PlotStyle)
                    for pi in plotStyle.mPlotItems:
                        self.plot2D.plotItem.removeItem(pi)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            def on3DPlotStyleRemoved(plotStyles):
                toRemove = []
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                    toRemove.append(plotStyle.mPlotItems)
                self.plot3D.removeItems(toRemove)
    
            self.plotSettingsModel2D.sigPlotStylesRemoved.connect(on2DPlotStyleRemoved)
            self.plotSettingsModel3D.sigPlotStylesRemoved.connect(on3DPlotStyleRemoved)
    
            #initialize the update loop
            self.updateRequested = True
            self.updateTimer = QTimer(self)
            self.updateTimer.timeout.connect(self.onDataUpdate)
            self.updateTimer.start(2000)
    
            self.sigMoveToDate.connect(self.onMoveToDate)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.initActions()
            #self.ui.stackedWidget.setCurrentPage(self.ui.pagePixel)
            self.ui.onStackPageChanged(self.ui.stackedWidget.currentIndex())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def temporalProfileLayer(self)->TemporalProfileLayer:
            """
            Returns a QgsVectorLayer that is used to store profile coordinates.
            :return:
            """
            return self.mTemporalProfileLayer
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onTemporalProfilesContextMenu(self, event):
            assert isinstance(event, QContextMenuEvent)