Skip to content
Snippets Groups Projects
profilevisualization.py 42.1 KiB
Newer Older
# -*- 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

DEBUG = False

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.sensorPxLayers = dict()
        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()
        print('Add {}'.format(sensor))
        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():
            if DEBUG:
                print('add {} to {}'.format(d, self))
            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()
    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)
        context = QgsExpressionContext()
        context.setFields(dp.fields())
        scope = QgsExpressionContextScope()

        possibleTsds = self.TS.getTSDs(sensorOfInterest=sensor)


        tsds = []
        values =  []

        if exp.isValid():
            mem.selectAll()
            for feature in mem.selectedFeatures():
                date = np.datetime64(feature.attribute('date'))
                scope.setFeature(feature)
                context.appendScope(scope)
                y = exp.evaluate(context)

                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)


        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)
        if DEBUG:
            print(t)