Skip to content
Snippets Groups Projects
profilevisualization.py 41.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -*- coding: utf-8 -*-
    """
    /***************************************************************************
                                  HUB TimeSeriesViewer
                                  -------------------
            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
    from __future__ import absolute_import
    
    import os, sys, pickle, datetime
    
    
    from qgis.gui import *
    from qgis.core import *
    from PyQt4.QtCore import *
    from PyQt4.QtXml import *
    from PyQt4.QtGui import *
    
    from timeseriesviewer import jp, SETTINGS
    
    from timeseriesviewer.timeseries import *
    from timeseriesviewer.utils import SpatialExtent, SpatialPoint, px2geo
    from timeseriesviewer.ui.docks import TsvDockWidgetBase, loadUi
    from timeseriesviewer.plotstyling import PlotStyle, PlotStyleButton
    from timeseriesviewer.pixelloader import PixelLoader, PixelLoaderResult
    
    import pyqtgraph as pg
    from osgeo import gdal, gdal_array
    import numpy as np
    
    
    def getTextColorWithContrast(c):
        assert isinstance(c, QColor)
        if c.lightness() < 0.5:
            return QColor('white')
        else:
            return QColor('black')
    
    class DateTimeAxis(pg.AxisItem):
    
            super(DateTimeAxis, self).__init__(*args, **kwds)
    
    
        def logTickStrings(self, values, scale, spacing):
            s = ""
    
        def tickStrings(self, values, scale, spacing):
            strns = []
    
    
            if len(values) == 0:
                return []
            #assert isinstance(values[0],
            values = [num2date(v) for v in values]
    
            ndays = rng.astype(int)
    
            strns = []
    
            for v in values:
                if ndays == 0:
                    strns.append(v.astype(str))
    
        def tickValues(self, minVal, maxVal, size):
    
            d = super(DateTimeAxis, self).tickValues(minVal, maxVal, size)
    
    class SensorPoints(pg.PlotDataItem):
    
        def __init__(self, *args, **kwds):
            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
    
    
    class PlotSettingsWidgetDelegate(QStyledItemDelegate):
    
        def __init__(self, tableView, parent=None):
    
            super(PlotSettingsWidgetDelegate, self).__init__(parent=parent)
            self._preferedSize = QgsFieldExpressionWidget().sizeHint()
            self.tableView = tableView
    
        def getColumnName(self, index):
            assert index.isValid()
            assert isinstance(index.model(), PlotSettingsModel)
            return index.model().columnames[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)
            if cname == 'y-value':
                w = QgsFieldExpressionWidget(parent)
                sv = self.tableView.model().data(index, Qt.UserRole)
                w.setLayer(sv.memLyr)
    
                w.setExpressionDialogTitle('Values sensor {}'.format(sv.sensor().name()))
                w.setToolTip('Set values shown for sensor {}'.format(sv.sensor().name()))
    
                w.fieldChanged.connect(lambda : self.checkData(w, w.expression()))
    
            elif cname == 'style':
                sv = self.tableView.model().data(index, Qt.UserRole)
    
    
                w = PlotStyleButton(parent)
                w.setPlotStyle(sv)
                w.setToolTip('Set sensor style.')
    
                w.sigPlotStyleChanged.connect(lambda: self.checkData(w, w.plotStyle()))
    
        def checkData(self, w, expression):
            if isinstance(w, QgsFieldExpressionWidget):
                assert expression == w.expression()
                assert w.isExpressionValid(expression) == 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)
            if cname == 'y-value':
                lastExpr = index.model().data(index, Qt.DisplayRole)
                assert isinstance(editor, QgsFieldExpressionWidget)
                editor.setProperty('lastexpr', lastExpr)
                editor.setField(lastExpr)
    
                style = index.data()
                assert isinstance(editor, PlotStyleButton)
                editor.setPlotStyle(style)
    
            else:
                raise NotImplementedError()
    
        def setModelData(self, w, model, index):
            cname = self.getColumnName(index)
            if cname == 'y-value':
                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.UserRole)
    
                assert isinstance(w, PlotStyleButton)
                model.setData(index, w.plotStyle(), Qt.UserRole)
    
    
    
    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()
    
    
    
    
        Object to store pixel data delivered by PixelLoader
    
        """
    
        sigSensorAdded = pyqtSignal(SensorInstrument)
        sigSensorRemoved = pyqtSignal(SensorInstrument)
        sigPixelAdded = pyqtSignal()
        sigPixelRemoved = pyqtSignal()
    
    
    
    
        def __init__(self, ):
    
            super(PixelCollection, self).__init__()
    
    
            self.memLyrCrs = QgsCoordinateReferenceSystem('EPSG:4326')
    
        def connectTimeSeries(self, timeSeries):
    
    
            if isinstance(timeSeries, TimeSeries):
                self.TS = timeSeries
    
                for sensor in self.TS.Sensors:
                    self.addSensor(sensor)
                self.TS.sigSensorAdded.connect(self.addSensor)
                self.TS.sigSensorRemoved.connect(self.removeSensor)
    
    
            else:
                self.TS = None
    
    
    
        def getFieldDefn(self, name, values):
            if isinstance(values, np.ndarray):
                # add bands
                if values.dtype in [np.int8, np.int16, np.int32, np.int64,
                                    np.uint8, np.uint16, np.uint32, np.uint64]:
                    fType = QVariant.Int
                    fTypeName = 'integer'
                elif values.dtype in [np.float16, np.float32, np.float64]:
                    fType = QVariant.Double
                    fTypeName = 'decimal'
            else:
                raise NotImplementedError()
    
            return QgsField(name, fType, fTypeName)
    
        def setFeatureAttribute(self, feature, name, value):
            assert isinstance(feature, QgsFeature)
            assert isinstance(name, str)
            i = feature.fieldNameIndex(name)
    
            assert i >= 0, 'Field "{}" does not exist'.format(name)
    
            field = feature.fields()[i]
            if field.isNumeric():
                if field.type() == QVariant.Int:
                    value = int(value)
                elif field.type() == QVariant.Double:
                    value = float(value)
                else:
                    raise NotImplementedError()
            feature.setAttribute(i, value)
    
    
        def addSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
    
            assert sensor not in self.sensorPxLayers.keys()
    
            mem = SensorPixelDataMemoryLayer(sensor, crs=self.memLyrCrs)
            self.sensorPxLayers[sensor] = mem
            self.sigSensorAdded.emit(sensor)
    
        def sensorData(self, sensor):
            assert isinstance(sensor, SensorInstrument)
            assert sensor in self.sensorPxLayers.keys()
            return self.sensorPxLayers[sensor]
    
        def removeSensor(self, sensor):
            if sensor in self.sensorPxLayers.keys():
                del self.sensorPxLayers[sensor]
    
    
            assert isinstance(d, PixelLoaderResult)
            if d.success():
                tsd = self.TS.getTSD(d.source)
                values = d.pxData
                nodata = np.asarray(d.noDataValue)
    
    
                nb, nl, ns = values.shape
                assert nb >= 1
    
                assert isinstance(tsd, TimeSeriesDatum)
    
    
                mem = self.sensorData(tsd.sensor)
    
    
                #insert each single pixel, line by line
    
                indicesY, indicesX = d.imagePixelIndices()
    
                gt = d.geoTransformation
                nb, nl, ns = d.pxData.shape
                srcCrs = d.imageCrs()
    
                for i in range(ns):
                    for j in range(nl):
    
                        if np.any(np.any(profile == nodata)):
                            continue
    
                        geo = px2geo(QPoint(indicesX[i], indicesY[i]), gt)
                        geo = SpatialPoint(srcCrs, geo).toCrs(self.memLyrCrs)
                        if not isinstance(geo, SpatialPoint):
                            continue
    
                        geometry = QgsPointV2(geo.x(), geo.y())
    
                        feature = QgsFeature(mem.fields())
    
                        #fnames = [f.name() for f in mem.fields()]
    
                        feature.setGeometry(QgsGeometry(geometry))
                        feature.setAttribute('date', str(tsd.date))
                        feature.setAttribute('doy', doy)
    
                        feature.setAttribute('geo_x', geo.x())
                        feature.setAttribute('geo_y', geo.y())
                        feature.setAttribute('px_x', indicesX[i])
                        feature.setAttribute('px_y', indicesY[i])
    
                        for iBand, bandIndex in enumerate(d.pxBandIndices):
                            name ='b{}'.format(bandIndex+1)
                            if profile.ndim == 1:
                                self.setFeatureAttribute(feature, name, profile[iBand])
                            else:
                                self.setFeatureAttribute(feature, name, profile[iBand,:])
    
                        mem.startEditing()
                        assert mem.addFeature(feature)
                        assert mem.commitChanges()
    
                #each pixel is a new feature
                self.sigPixelAdded.emit()
    
            pass
    
    
    
        def clear(self):
            self.sensorPxLayers.clear()
    
        def clearPixels(self):
            sensors = self.sensorPxLayers.keys()
            n_deleted = 0
            for sensor in sensors:
                mem = self.sensorPxLayers[sensor]
                assert mem.startEditing()
                mem.selectAll()
                b, n = mem.deleteSelectedFeatures()
                n_deleted += n
                assert mem.commitChanges()
    
    
                #self.sigSensorRemoved.emit(sensor)
    
    
            if n_deleted > 0:
                self.sigPixelRemoved.emit()
    
    
        def dateValues(self, sensor, expression):
    
            mem = self.sensorData(sensor)
    
            dp = mem.dataProvider()
            exp = QgsExpression(expression)
            exp.prepare(dp.fields())
    
            possibleTsds = self.TS.getTSDs(sensorOfInterest=sensor)
    
    
            tsds = []
            values =  []
    
            if exp.isValid():
                mem.selectAll()
                for feature in mem.selectedFeatures():
                    date = np.datetime64(feature.attribute('date'))
                    y = exp.evaluatePrepared(feature)
                    if y is not None:
                        tsd = next(tsd for tsd in possibleTsds if tsd.date == date)
                        tsds.append(tsd)
                        values.append(y)
    
    class SensorPlotStyle(PlotStyle):
    
        def __init__(self):
            super(SensorPlotStyle, self).__init__()
    
            self.mSensor = None
            self.memLyr = None
            self.mExpression = u'"b1"'
            self.mIsVisible = True
    
        def connectSensor(self, sensor, memoryLayer):
    
            assert isinstance(sensor, SensorInstrument)
    
            assert isinstance(memoryLayer, QgsVectorLayer)
    
            self.memLyr = memoryLayer
    
    
        def isValid(self):
            """
            :return: True, if connected to a sensor and memoryLayer that contains pixel values
            """
            return isinstance(self.memLyr, QgsVectorLayer) and isinstance(self.mSensor, SensorInstrument)
    
        def sensor(self):
            return self.mSensor
    
        def setVisibility(self, b):
            self.mIsVisible
        def isVisible(self):
            return self.mIsVisible
    
        def setExpression(self, exp):
            self.mExpression = exp
    
        def expression(self):
            return self.mExpression
    
    
        def __reduce_ex__(self, protocol):
            return self.__class__, (), self.__getstate__()
    
        def __getstate__(self):
            result = super(SensorPlotStyle, self).__getstate__()
            #remove
            del result['memLyr']
            del result['mSensor']
    
            return result
    
    
    
    class DateTimeViewBox(pg.ViewBox):
        """
        Subclass of ViewBox
        """
        sigMoveToDate = pyqtSignal(np.datetime64)
        def __init__(self, parent=None):
            """
            Constructor of the CustomViewBox
            """
            super(DateTimeViewBox, self).__init__(parent)
            #self.menu = None # Override pyqtgraph ViewBoxMenu
            #self.menu = self.getMenu() # Create the menu
            #self.menu = None
    
    
        def raiseContextMenu(self, ev):
    
            pt = self.mapDeviceToView(ev.pos())
    
            print(pt.x(), pt.y())
            date = num2date(pt.x())
            menu = QMenu(None)
            a = menu.addAction('Move to {}'.format(date))
            a.setData(date)
            a.triggered.connect(lambda : self.sigMoveToDate.emit(date))
            self.scene().addParentContextMenus(self, menu, ev)
            menu.exec_(ev.screenPos().toPoint())
    
    
    
    
    
    class DateTimePlotWidget(pg.PlotWidget):
        """
        Subclass of PlotWidget
        """
        def __init__(self, parent=None):
            """
            Constructor of the widget
            """
            super(DateTimePlotWidget, self).__init__(parent, viewBox=DateTimeViewBox())
            self.plotItem = pg.PlotItem(axisItems={'bottom':DateTimeAxis(orientation='bottom')}, viewBox=DateTimeViewBox())
            self.setCentralItem(self.plotItem)
    
    
    
    class PlotSettingsModel(QAbstractTableModel):
    
    
        #sigSensorAdded = pyqtSignal(SensorPlotSettings)
    
        sigVisibilityChanged = pyqtSignal(SensorPlotStyle)
        sigDataChanged = pyqtSignal(SensorPlotStyle)
    
    
        columnames = ['sensor','nb','style','y-value']
    
        def __init__(self, pixelCollection, parent=None, *args):
    
    
            #assert isinstance(tableView, QTableView)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            super(PlotSettingsModel, self).__init__(parent=parent)
    
            assert isinstance(pixelCollection, PixelCollection)
    
    
            self.sortColumnIndex = 0
            self.sortOrder = Qt.AscendingOrder
    
            self.pxCollection = pixelCollection
    
    
            self.pxCollection.sigSensorAdded.connect(self.addSensor)
    
            self.pxCollection.sigSensorRemoved.connect(self.removeSensor)
    
            for sensor in self.pxCollection.sensorPxLayers.keys():
                self.addSensor(sensor)
    
    
            self.sort(0, Qt.AscendingOrder)
            s = ""
            self.dataChanged.connect(self.signaler)
    
        def testSlot(self, *args):
            print('TESTSLOT')
            s = ""
    
    
        def signaler(self, idxUL, idxLR):
            if idxUL.isValid():
    
                sensorView = self.getSensorPlotSettingsFromIndex(idxUL)
    
                cname = self.columnames[idxUL.column()]
    
                if cname in ['sensor','style']:
    
                    self.sigVisibilityChanged.emit(sensorView)
                if cname in ['y-value']:
                    self.sigDataChanged.emit(sensorView)
    
    
        def requiredBands(self, sensor):
            """
            Returns the band indices required to calculate the values for this sensor
            :param sensor:
            :return: [list-of-band-indices]
            """
            idx = self.getIndexFromSensor(sensor)
            idx = self.createIndex(idx.row(),self.columnames.index('y-value'))
    
            equation = self.data(idx)
            plotSettings = self.data(idx, Qt.UserRole)
    
            assert isinstance(plotSettings, SensorPlotStyle)
    
            expression = plotSettings.expression()
    
            fields = plotSettings.memLyr.fields()
    
            bandNames = []
            bandIndices = []
            LUT_Field2Band = dict()
            for field in fields:
                assert isinstance(field, QgsField)
                LUT_Field2Band[field.name()] = field.name()
                if len(field.alias()) > 0:
                    LUT_Field2Band[field.alias()] = field.name()
    
    
            for name, fieldName in LUT_Field2Band.items():
                if re.search(name+'($|[^\d])', expression):
                    bandNames.append(fieldName)
                    continue
    
            for bandName in bandNames:
                if re.search('b\d+', bandName):
                    bandIndices.append(int(bandName[1:])-1)
            return bandIndices
    
    
    
    
    
    
    
        def addSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
    
            index = 'DEFAULT'
    
            sensorSettings = self.restorePlotSettings(sensor, index=index)
    
            if not isinstance(sensorSettings, SensorPlotStyle):
                sensorSettings = SensorPlotStyle()
            sensorSettings.connectSensor(sensor, self.pxCollection.sensorPxLayers[sensor])
    
            i = len(self.mSensorPlotSettings)
    
    
            self.beginInsertRows(QModelIndex(),i,i)
    
            self.mSensorPlotSettings.append(sensorSettings)
    
        def removeSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
    
            toRemove = [s for s in self.mSensorPlotSettings if s.sensor() == sensor]
    
                idx = self.getIndexFromSensor(s.sensor())
    
                self.beginRemoveRows(QModelIndex(), idx.row(),idx.row())
    
                self.mSensorPlotSettings.remove(s)
    
        def onSensorNameChanged(self, name):
            self.beginResetModel()
    
    
        def sort(self, col, order):
            if self.rowCount() == 0:
                return
    
    
            colName = self.columnames[col]
            r = order != Qt.AscendingOrder
    
            #self.beginMoveRows(idxSrc,
    
            if colName == 'sensor':
    
                self.mSensorPlotSettings.sort(key = lambda sv:sv.sensor.name(), reverse=r)
    
                self.mSensorPlotSettings.sort(key=lambda sv: sv.sensor.nb, reverse=r)
    
                self.mSensorPlotSettings.sort(key=lambda sv: sv.expression, reverse=r)
    
                self.mSensorPlotSettings.sort(key=lambda sv: sv.color, reverse=r)
    
            return len(self.mSensorPlotSettings)
    
    
    
        def removeRows(self, row, count , parent=QModelIndex()):
            self.beginRemoveRows(parent, row, row+count-1)
    
            toRemove = self.mSensorPlotSettings[row:row + count]
    
                self.mSensorPlotSettings.remove(tsd)
    
        def getIndexFromSensor(self, sensor):
    
            assert isinstance(sensor, SensorInstrument)
    
            sensorViews = [i for i, s in enumerate(self.mSensorPlotSettings) if s.sensor() == sensor]
    
            assert len(sensorViews) == 1
            return self.createIndex(sensorViews[0],0)
    
    
        def getSensorPlotSettingsFromIndex(self, index):
    
                return self.mSensorPlotSettings[index.row()]
    
            return None
    
        def columnCount(self, parent = QModelIndex()):
            return len(self.columnames)
    
        def data(self, index, role = Qt.DisplayRole):
            if role is None or not index.isValid():
                return None
    
            value = None
            columnName = self.columnames[index.column()]
    
            sw = self.getSensorPlotSettingsFromIndex(index)
            sensor = sw.sensor()
    
            #print(('data', columnName, role))
            if role == Qt.DisplayRole:
                if columnName == 'sensor':
    
            elif role == Qt.CheckStateRole:
                if columnName == 'sensor':
    
                    value = Qt.Checked if sw.isVisible() else Qt.Unchecked
    
            elif role == Qt.UserRole:
                value = sw
            #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.columnames[index.column()]
    
    
            sw = self.getSensorPlotSettingsFromIndex(index)
    
            assert isinstance(sw, SensorPlotStyle)
    
            if role in [Qt.DisplayRole, Qt.EditRole]:
                if columnName == 'y-value':
    
                    result = True
                elif columnName == 'style':
    
                    if isinstance(value, PlotStyle):
    
                        sw.plotStyle.copyFrom(value)
    
    
                        result = True
    
            if role == Qt.CheckStateRole:
                if columnName == 'sensor':
    
                    sw.setVisibility(value == Qt.Checked)
    
            if role == Qt.UserRole:
                if columnName == 'y-value':
    
                    result = True
                elif columnName == 'style':
    
                    sw.copyFrom(value)
                    result = True
    
                #save plot-style
                self.savePlotSettings(sw, index='DEFAULT')
    
                self.dataChanged.emit(index, index)
    
            return result
    
    
        def savePlotSettings(self, sensorPlotSettings, index='DEFAULT'):
    
            assert isinstance(sensorPlotSettings, SensorPlotStyle)
    
            id = 'SPS.{}.{}'.format(index, sensorPlotSettings.sensor().id())
            d = pickle.dumps(sensorPlotSettings)
            SETTINGS.setValue(id, d)
    
        def restorePlotSettings(self, sensor, index='DEFAULT'):
            assert isinstance(sensor, SensorInstrument)
            id = 'SPS.{}.{}'.format(index, sensor.id())
    
            sensorPlotSettings = SETTINGS.value(id)
            if sensorPlotSettings is not None:
                try:
                    sensorPlotSettings = pickle.loads(sensorPlotSettings)
                    s = ""
                except:
                    sensorPlotSettings = None
                    pass
    
            if isinstance(sensorPlotSettings, SensorPlotStyle):
                return sensorPlotSettings
    
        def flags(self, index):
            if index.isValid():
                columnName = self.columnames[index.column()]
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
                if columnName == 'sensor':
                    flags = flags | Qt.ItemIsUserCheckable
    
    
                if columnName in ['y-value','style']: #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.columnames[col]
            elif orientation == Qt.Vertical and role == Qt.DisplayRole:
                return col
            return None
    
    
    
    
    class ProfileViewDockUI(TsvDockWidgetBase, loadUi('profileviewdock.ui')):
    
        def __init__(self, parent=None):
            super(ProfileViewDockUI, self).__init__(parent)
            self.setupUi(self)
            from timeseriesviewer import OPENGL_AVAILABLE, SETTINGS
    
    
            #TBD.
            self.line.setVisible(False)
            self.listWidget.setVisible(False)
            self.stackedWidget.setCurrentWidget(self.page2D)
    
    
            if OPENGL_AVAILABLE:
                l = self.page3D.layout()
                l.removeWidget(self.labelDummy3D)
                from pyqtgraph.opengl import GLViewWidget
                self.plotWidget3D = GLViewWidget(self.page3D)
                l.addWidget(self.plotWidget3D)
            else:
                self.plotWidget3D = None
    
    
            #pi = self.plotWidget2D.plotItem
            #ax = DateAxis(orientation='bottom', showValues=True)
            #pi.layout.addItem(ax, 3,2)
    
    
            self.baseTitle = self.windowTitle()
            self.TS = None
    
            self.progressBar.setMinimum(0)
            self.progressBar.setMaximum(100)
            self.progressBar.setValue(0)
            self.progressInfo.setText('')
            self.pxViewModel2D = None
            self.pxViewModel3D = None
    
            self.tableView2DBands.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
            self.tableView2DBands.setSortingEnabled(True)
            self.btnRefresh2D.setDefaultAction(self.actionRefresh2D)
    
    
    
    
    def date2num(d):
        d2 = d.astype(datetime.datetime)
        o = d2.toordinal()
    
        #assert d == num2date(o)
    
        return o
    
        n = int(np.round(n))
    
        if n < 1:
            n = 1
        d = datetime.date.fromordinal(n)
        return np.datetime64(d, 'D')
    
    
    class SpectralTemporalVisualization(QObject):
    
        sigShowPixel = pyqtSignal(TimeSeriesDatum, QgsPoint, QgsCoordinateReferenceSystem)
    
    
        """
        Signalizes to move to specific date of interest
        """
    
        sigMoveToDate = pyqtSignal(np.datetime64)
    
            super(SpectralTemporalVisualization, self).__init__()
            #assert isinstance(timeSeries, TimeSeries)
    
    
            if not isinstance(ui, ProfileViewDockUI):
                print('UI : {}'.format(ui))
    
            assert isinstance(ui, ProfileViewDockUI), 'arg ui of type: {} {}'.format(type(ui), str(ui))
    
            self.pixelLoader = PixelLoader()
            self.pixelLoader.sigPixelLoaded.connect(self.onPixelLoaded)
            self.pixelLoader.sigLoadingStarted.connect(lambda: self.ui.progressInfo.setText('Start loading...'))
    
            self.plot_initialized = False
            self.TV = ui.tableView2DBands
    
            self.TV.setSortingEnabled(False)
    
            self.plot2D = ui.plotWidget2D
    
            self.plot2D.plotItem.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
    
    
            self.plot3D = ui.plotWidget3D
    
            self.pxCollection = PixelCollection()
            self.pxCollection.sigPixelAdded.connect(self.requestUpdate)
            self.pxCollection.sigPixelRemoved.connect(self.clear)
    
            self.plotSettingsModel = None
    
            self.pixelLoader.sigLoadingStarted.connect(self.clear)
            self.pixelLoader.sigLoadingFinished.connect(lambda : self.plot2D.enableAutoRange('x', False))
            self.ui.actionRefresh2D.triggered.connect(lambda: self.setData())
    
            # self.VIEW.setItemDelegateForColumn(3, PointStyleDelegate(self.VIEW))
            self.plotData2D = dict()
            self.plotData3D = dict()
    
            self.updateRequested = True
            self.updateTimer = QTimer(self)
            self.updateTimer.timeout.connect(self.updatePlot)
            self.updateTimer.start(2000)
    
            self.sigMoveToDate.connect(self.onMoveToDate)
    
        def connectTimeSeries(self, TS):
    
            assert isinstance(TS, TimeSeries)
            self.TS = TS
    
            self.pxCollection.connectTimeSeries(self.TS)
    
            self.TS.sigSensorRemoved.connect(self.removeSensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.plotSettingsModel = PlotSettingsModel(self.pxCollection, parent=self)
    
            self.plotSettingsModel.sigVisibilityChanged.connect(self.setVisibility)
    
            self.plotSettingsModel.sigDataChanged.connect(self.requestUpdate)
    
            self.plotSettingsModel.rowsInserted.connect(self.onRowsInserted)
    
            # self.plotSettingsModel.modelReset.connect(self.updatePersistantWidgets)
    
            self.TV.setModel(self.plotSettingsModel)
            self.delegate = PlotSettingsWidgetDelegate(self.TV)
            self.TV.setItemDelegateForColumn(2, self.delegate)
            self.TV.setItemDelegateForColumn(3, self.delegate)
    
            # self.TV.setItemDelegateForColumn(3, PointStyleDelegate(self.TV))
    
        sigMoveToTSD = pyqtSignal(TimeSeriesDatum)
    
        def onMoveToDate(self, date):
            dt = np.asarray([np.abs(tsd.date - date) for tsd in self.TS])
            i = np.argmin(dt)
            self.sigMoveToTSD.emit(self.TS[i])
    
    
        def onPixelLoaded(self, nDone, nMax, d):
            self.ui.progressBar.setValue(nDone)
            self.ui.progressBar.setMaximum(nMax)
    
            assert isinstance(d, PixelLoaderResult)
    
    
            QgsApplication.processEvents()
            bn = os.path.basename(d.source)
            if d.success():
    
                t = 'Last loaded from {}.'.format(bn)
                self.pxCollection.addPixel(d)
            else:
                t = 'Failed loading from {}.'.format(bn)
    
                if d.info and d.info != '':
                    t += '({})'.format(d.info)
    
            self.ui.progressInfo.setText(t)
    
    
        def requestUpdate(self, *args):
            self.updateRequested = True
            #next time