Skip to content
Snippets Groups Projects
profilevisualization.py 103 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 collections import OrderedDict
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
Benjamin Jakimow's avatar
Benjamin Jakimow committed
from timeseriesviewer.pixelloader import PixelLoader, PixelLoaderTask
from timeseriesviewer.sensorvisualization import SensorListModel

Benjamin Jakimow's avatar
Benjamin Jakimow committed
from pyqtgraph import functions as fn
from pyqtgraph import AxisItem


import datetime

from osgeo import gdal, gdal_array
import numpy as np

DEBUG = False

LABEL_DN = 'DN or Index'
LABEL_TIME = 'Date'


OPENGL_AVAILABLE = False

try:
    import OpenGL
    OPENGL_AVAILABLE = True

    from pyqtgraph.opengl import GLViewWidget


    class ViewWidget3D(GLViewWidget):

        def paintGL(self, *args, **kwds):
            GLViewWidget.paintGL(self, *args, **kwds)
            self.qglColor(Qt.white)
            self.renderAnnotations()

        def renderAnnotations(self):

            #self.renderText(0.8, 0.8, 0.8, 'text')
            self.renderText(5, 10, 'text')
    """
    class TemporalProfileGLLinePlotItem(gl.GLLinePlotItem):

        def __init__(self, plotStyle, *args, **kwds):
            assert isinstance(plotStyle, TemporalProfile3DPlotStyle)

        gl.GLLinePlotItem
    """
    if DEBUG:
        print('unable to import package OpenGL')
def getTextColorWithContrast(c):
    assert isinstance(c, QColor)
    if c.lightness() < 0.5:
        return QColor('white')
    else:
        return QColor('black')
def bandIndex2bandKey(i):
    assert isinstance(i, int)
    assert i >= 0
    return 'b{}'.format(i + 1)

def bandKey2bandIndex(key):
    match = PlotSettingsModel2D.regBandKeyExact.search(key)
    assert match
    idx = int(match.group()[1:]) - 1
    return idx

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()

class DateTimeAxis(pg.AxisItem):
        super(DateTimeAxis, self).__init__(*args, **kwds)
        self.setRange(1,3000)
        self.enableAutoSIPrefix(False)
        self.labelAngle = 0
    def logTickStrings(self, values, scale, spacing):
        s = ""

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def tickStrings(self, values, scale, spacing):
        strns = []

        if len(values) == 0:
            return []
        #assert isinstance(values[0],

        values = [num2date(v) if v > 0 else num2date(1) 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)
    def drawPicture(self, p, axisSpec, tickSpecs, textSpecs):


        p.setRenderHint(p.Antialiasing, False)
        p.setRenderHint(p.TextAntialiasing, True)

        ## draw long line along axis
        pen, p1, p2 = axisSpec
        p.setPen(pen)
        p.drawLine(p1, p2)
        p.translate(0.5, 0)  ## resolves some damn pixel ambiguity

        ## draw ticks
        for pen, p1, p2 in tickSpecs:
            p.setPen(pen)
            p.drawLine(p1, p2)


        ## Draw all text
        if self.tickFont is not None:
            p.setFont(self.tickFont)
        p.setPen(self.pen())

        #for rect, flags, text in textSpecs:
        #    p.drawText(rect, flags, text)
        #    # p.drawRect(rect)
        #see https://github.com/pyqtgraph/pyqtgraph/issues/322
        for rect, flags, text in textSpecs:
            p.save()  # save the painter state
            p.translate(rect.center())   # move coordinate system to center of text rect
            p.rotate(self.labelAngle)  # rotate text
            p.translate(-rect.center())  # revert coordinate system
            p.drawText(rect, flags, text)
            p.restore()  # restore the painter state
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 TemporalProfilePlotDataItem(pg.PlotDataItem):

    def __init__(self, plotStyle, parent=None):
        assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
        super(TemporalProfilePlotDataItem, self).__init__([], [], parent=parent)
        self.menu = None
        #self.setFlags(QGraphicsItem.ItemIsSelectable)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.mPlotStyle = plotStyle
        self.setAcceptedMouseButtons(Qt.LeftButton | Qt.RightButton)
        self.mPlotStyle.sigUpdated.connect(self.updateDataAndStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.updateStyle()

    # 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 = QMenu()
            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


    def updateDataAndStyle(self):

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        TP = self.mPlotStyle.temporalProfile()
        sensor = self.mPlotStyle.sensor()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if isinstance(TP, TemporalProfile) and isinstance(sensor, SensorInstrument):
            x, y = TP.dataFromExpression(self.mPlotStyle.sensor(), self.mPlotStyle.expression())
            x =  np.asarray(x, dtype=np.float)
            y = np.asarray(y, dtype=np.float)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if len(y) > 0:
                self.setData(x=x, y=y)
            else:
                self.setData(x=[], y=[]) #dummy
        self.updateStyle()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def updateStyle(self):
        """
        Updates visibility properties
        """

        if DEBUG:
            print('{} updateStyle'.format(self))
        from pyqtgraph.graphicsItems.ScatterPlotItem import drawSymbol
#        path = drawSymbol(p, self.markerSymbol, self.markerSize, self.markerPen, self.markerBrush)
    #                    #painter, symbol, size, pen, brush
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.setVisible(self.mPlotStyle.isVisible())
        self.setSymbol(self.mPlotStyle.markerSymbol)
        self.setSymbolSize(self.mPlotStyle.markerSize)
        self.setSymbolBrush(self.mPlotStyle.markerBrush)
        self.setSymbolPen(self.mPlotStyle.markerPen)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.setPen(self.mPlotStyle.linePen)
        self.update()

        #self.setPen(fn.mkPen(self.mPlotStyle.linePen))
        #self.setFillBrush(fn.mkBrush(self.mPlotStyle.mExpression))
        #self.setSymbolBrush(fn.mkBrush(self.mPlotStyle.markerBrush))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        # self.setFillBrush(self.mPlotStyle.)

        #self.update()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def setClickable(self, b, width=None):
        assert isinstance(b, bool)
        self.curve.setClickable(b, width=width)

    def setColor(self, color):
        if not isinstance(color, QColor):

            color = QColor(color)
        self.setPen(color)

    def pen(self):
        return fn.mkPen(self.opts['pen'])

    def color(self):
        return self.pen().color()


    def setLineWidth(self, width):
        pen = pg.mkPen(self.opts['pen'])
        assert isinstance(pen, QPen)
        pen.setWidth(width)
        self.setPen(pen)



class PlotSettingsModel2DWidgetDelegate(QStyledItemDelegate):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    """
    def __init__(self, tableView, timeSeries, temporalProfileListModel, parent=None):
        super(PlotSettingsModel2DWidgetDelegate, self).__init__(parent=parent)
        self._preferedSize = QgsFieldExpressionWidget().sizeHint()
        self.mTableView = tableView
        self.mTimeSeries = timeSeries
        self.mTemporalProfileListModel = temporalProfileListModel
        self.mSensorLayers = {}
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def setItemDelegates(self, tableView):
        assert isinstance(tableView, QTableView)
        model = tableView.model()

        assert isinstance(model, PlotSettingsModel2D)
        for c in [model.cnSensor, model.cnExpression, model.cnStyle, model.cnTemporalProfile]:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            i = model.columNames.index(c)
            tableView.setItemDelegateForColumn(i, self)

    def getColumnName(self, index):
        assert index.isValid()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        model = index.model()
        assert isinstance(model, PlotSettingsModel2D)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return 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 exampleLyr(self, sensor):
        assert isinstance(sensor, SensorInstrument)


        if sensor not in self.mSensorLayers.keys():

            crs = QgsCoordinateReferenceSystem('EPSG:4862')
            uri = 'Point?crs={}'.format(crs.authid())
            lyr = QgsVectorLayer(uri, 'LOCATIONS', 'memory', False)
            f = sensorExampleQgsFeature(sensor)
            assert isinstance(f, QgsFeature)
            assert lyr.startEditing()
            for field in f.fields():
                lyr.addAttribute(field)
            lyr.addFeature(f)
            lyr.commitChanges()
            self.mSensorLayers[sensor] = lyr
        return self.mSensorLayers[sensor]

    def createEditor(self, parent, option, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            plotStyle = model.idx2plotStyle(index)
            if isinstance(plotStyle, TemporalProfile2DPlotStyle):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if cname == model.cnExpression:
                    w = QgsFieldExpressionWidget(parent=parent)
                    w.setExpression(plotStyle.expression())
                    w.setLayer(self.exampleLyr(plotStyle.sensor()))
                    plotStyle.sigSensorChanged.connect(lambda s : w.setLayer(self.exampleLyr(s)))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    w.setExpressionDialogTitle('Values')
                    w.setToolTip('Set an expression to specify the image band or calculate a spectral index.')
                    w.fieldChanged[str,bool].connect(lambda n, b : self.checkData(index, w, w.expression()))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

                elif cname == model.cnStyle:
                    w = PlotStyleButton(parent=parent)
                    w.setPlotStyle(plotStyle)
                    w.setToolTip('Set style.')
                    w.sigPlotStyleChanged.connect(lambda: self.checkData(index, w, w.plotStyle()))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

                elif cname == model.cnSensor:
                    w = QComboBox(parent=parent)
                    m = SensorListModel(self.mTimeSeries)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    w.setModel(m)

                elif cname == model.cnTemporalProfile:
                    w = QComboBox(parent=parent)
                    w.setModel(self.mTemporalProfileListModel)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                else:
                    raise NotImplementedError()
    def checkData(self, index, w, expression):
        assert isinstance(index, QModelIndex)
        model = self.mTableView.model()
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
            plotStyle = model.idx2plotStyle(index)
            assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
            if isinstance(w, QgsFieldExpressionWidget):
                assert 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)
        model = self.mTableView.model()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            cname = self.getColumnName(index)
            if cname == model.cnExpression:
                lastExpr = index.model().data(index, Qt.DisplayRole)
                assert isinstance(editor, QgsFieldExpressionWidget)
                editor.setProperty('lastexpr', lastExpr)
                editor.setField(lastExpr)

            elif cname == model.cnStyle:
                style = index.data()
                assert isinstance(editor, PlotStyleButton)
                editor.setPlotStyle(style)

            elif cname == model.cnSensor:
                assert isinstance(editor, QComboBox)
                m = editor.model()
                assert isinstance(m, SensorListModel)
                sensor = index.data(role=Qt.UserRole)
                if isinstance(sensor, SensorInstrument):
                    idx = m.sensor2idx(sensor)
                    editor.setCurrentIndex(idx.row())
            elif cname == model.cnTemporalProfile:
                assert isinstance(editor, QComboBox)
                m = editor.model()
                assert isinstance(m, TemporalProfileCollectionListModel)
                TP = index.data(role=Qt.UserRole)
                if isinstance(TP, TemporalProfile):
                    idx = m.tp2idx(TP)
                    editor.setCurrentIndex(idx)
            else:
                raise NotImplementedError()

    def setModelData(self, w, model, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if cname == model.cnExpression:
                assert isinstance(w, QgsFieldExpressionWidget)
                expr = w.asExpression()
                exprLast = model.data(index, Qt.DisplayRole)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if w.isValidExpression() and expr != exprLast:
                    model.setData(index, w.asExpression(), Qt.EditRole)

            elif cname == model.cnStyle:
                assert isinstance(w, PlotStyleButton)
                model.setData(index, w.plotStyle(), Qt.EditRole)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            elif cname == model.cnSensor:
                assert isinstance(w, QComboBox)
                sensor = w.itemData(w.currentIndex(), role=Qt.UserRole)
                assert isinstance(sensor, SensorInstrument)
                model.setData(index, sensor, Qt.EditRole)

            elif cname == model.cnTemporalProfile:
                assert isinstance(w, QComboBox)
                TP = w.itemData(w.currentIndex(), role=Qt.UserRole)
                assert isinstance(TP, TemporalProfile)
                model.setData(index, TP, Qt.EditRole)

            else:
                raise NotImplementedError()

class PlotSettingsModel3DWidgetDelegate(QStyledItemDelegate):
    """

    """
    def __init__(self, tableView, parent=None):

        super(PlotSettingsModel3DWidgetDelegate, self).__init__(parent=parent)
        self._preferedSize = QgsFieldExpressionWidget().sizeHint()
        self.mTableView = tableView



    def setItemDelegates(self, tableView):
        assert isinstance(tableView, QTableView)
        model = tableView.model()

        assert isinstance(model, PlotSettingsModel3D)
        for c in [model.cnStyle]:
            i = model.columNames.index(c)
            tableView.setItemDelegateForColumn(i, self)

    def getColumnName(self, index):
        assert index.isValid()
        model = index.model()
        assert isinstance(model, PlotSettingsModel3D)
        return 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)
        model = self.mTableView.model()
        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            plotStyle = model.idx2plotStyle(index)
            if isinstance(plotStyle, TemporalProfile3DPlotStyle):
                if cname == model.cnStyle:
                    w = QgsColorButton(parent=parent)
                    w.setColor(plotStyle.color())
                    w.setToolTip('Set line color')
                    w.colorChanged.connect(lambda: self.checkData(index, w))

        return w

    def checkData(self, index, w):
        assert isinstance(index, QModelIndex)
        model = self.mTableView.model()
        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            plotStyle = model.idx2plotStyle(index)
            assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
            if isinstance(w, QgsColorButton):
                self.commitData.emit(w)

    def setEditorData(self, editor, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()

        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            style = model.idx2plotStyle(index)
            assert isinstance(style, TemporalProfile3DPlotStyle)
            cname = self.getColumnName(index)
            if cname == model.cnStyle:
                assert isinstance(editor, QgsColorButton)
                editor.setColor(style.color())
            else:
                raise NotImplementedError()

    def setModelData(self, w, model, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()

        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            if cname == model.cnStyle:
                assert isinstance(w, QgsColorButton)
                model.setData(index, w.color(), Qt.EditRole)
            else:
                raise NotImplementedError()


class SensorPixelDataMemoryLayer(QgsVectorLayer):

    def __init__(self, sensor, crs=None):
        assert isinstance(sensor, SensorInstrument)
        if crs is None:
            crs = QgsCoordinateReferenceSystem('EPSG:4862')
        uri = 'Point?crs={}'.format(crs.authid())
        super(SensorPixelDataMemoryLayer, self).__init__(uri, 'Pixels_sensor_' + sensor.name(), 'memory', False)
        self.mSensor = sensor

        #initialize fields
        assert self.startEditing()
        # standard field names, types, etc.
        fieldDefs = [('pxid', QVariant.String, 'integer'),
                     ('date', QVariant.String, 'char'),
                     ('doy', QVariant.Int, 'integer'),
                     ('geo_x', QVariant.Double, 'decimal'),
                     ('geo_y', QVariant.Double, 'decimal'),
                     ('px_x', QVariant.Int, 'integer'),
                     ('px_y', QVariant.Int, 'integer'),
                     ]
        # one field for each band
        for b in range(sensor.nb):
            fName = 'b{}'.format(b + 1)
            fieldDefs.append((fName, QVariant.Double, 'decimal'))

        # initialize fields
        for fieldDef in fieldDefs:
            field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
            self.addAttribute(field)
        self.commitChanges()

    def sensor(self):
        return self.mSensor

    def nPixels(self):
        raise NotImplementedError()

    def dates(self):
        raise NotImplementedError()


Benjamin Jakimow's avatar
Benjamin Jakimow committed
class TemporalProfileCollectionListModel(QAbstractListModel):


    def __init__(self, temporalProfileCollection, *args, **kwds):

        super(TemporalProfileCollectionListModel, self).__init__(*args, **kwds)
        assert isinstance(temporalProfileCollection, TemporalProfileCollection)

        self.mTPColl = temporalProfileCollection
        self.mTPColl.rowsAboutToBeInserted.connect(self.rowsAboutToBeInserted)
        self.mTPColl.rowsInserted.connect(self.rowsInserted.emit)
        #self.mTPColl.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved)
        self.mTPColl.rowsRemoved.connect(lambda : self.modelReset.emit())

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def idx2tp(self, *args, **kwds):
        return self.mTPColl.idx2tp(*args, **kwds)

    def tp2idx(self, *args, **kwds):
        return self.mTPColl.tp2idx(*args, **kwds)

    def flags(self, index):
        if index.isValid():
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
            return flags
            #return item.qt_flags(index.column())
        return Qt.NoItemFlags

    def rowCount(self, *args, **kwds):
        return self.mTPColl.rowCount(*args, **kwds)


    def data(self, index, role=Qt.DisplayRole):
        if role is None or not index.isValid():
            return None



        TP = self.mTPColl.idx2tp(index)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        value = None
        if isinstance(TP, TemporalProfile):
            if role == Qt.DisplayRole:
                value = '{}'.format(TP.name())
            elif role == Qt.ToolTipRole:
                value = '#{} "{}" {}'.format(TP.mID, TP.name(), TP.mCoordinate)
            elif role == Qt.UserRole:
                value = TP

        return value

class TemporalProfileCollection(QAbstractTableModel):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    A collection to store the TemporalProfile data delivered by a PixelLoader
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    #sigSensorAdded = pyqtSignal(SensorInstrument)
    #sigSensorRemoved = pyqtSignal(SensorInstrument)
    #sigPixelAdded = pyqtSignal()
    #sigPixelRemoved = pyqtSignal()
    sigTemporalProfilesAdded = pyqtSignal(list)
    sigTemporalProfilesRemoved = pyqtSignal(list)
    sigMaxProfilesChanged = pyqtSignal(int)
    def __init__(self, ):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        super(TemporalProfileCollection, self).__init__()
        #self.sensorPxLayers = dict()
        #self.memLyrCrs = QgsCoordinateReferenceSystem('EPSG:4326')
        self.newDataFlag = False

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.mcnID = 'id'
        self.mcnCoordinate = 'Coordinate'
        self.mcnLoaded = 'Loading'
        self.mcnName = 'Name'
        self.mColumNames = [self.mcnName, self.mcnLoaded, self.mcnCoordinate]

        crs = QgsCoordinateReferenceSystem('EPSG:4862')
        uri = 'Point?crs={}'.format(crs.authid())

        self.TS = None
        self.mLocations = QgsVectorLayer(uri, 'LOCATIONS', 'memory', False)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.mTemporalProfiles = []
        self.mTPLookupSpatialPoint = {}
        self.mTPLookupID = {}
        self.mCurrentTPID = 0
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.mMaxProfiles = 10

        self.nextID = 0

    def __len__(self):
        return len(self.mTemporalProfiles)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __iter__(self):
        return iter(self.mTemporalProfiles)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __getitem__(self, slice):
        return self.mTemporalProfiles[slice]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __contains__(self, item):
        return item in self.mTemporalProfiles

    def rowCount(self, parent=None, *args, **kwargs):
        return len(self.mTemporalProfiles)

    def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
        return len(self.mColumNames)

    def idx2tp(self, index):
        if index.isValid() and index.row() < len(self.mTemporalProfiles) :
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            return self.mTemporalProfiles[index.row()]
        return None

    def tp2idx(self, temporalProfile):
        assert isinstance(temporalProfile, TemporalProfile)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if temporalProfile in self.mTemporalProfiles:
            row = self.mTemporalProfiles.index(temporalProfile)
            return self.createIndex(row, 0)
        else:
            return QModelIndex()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def data(self, index, role = Qt.DisplayRole):
        if role is None or not index.isValid():
            return None

        value = None
        columnName = self.mColumNames[index.column()]
        TP = self.idx2tp(index)
        if not isinstance(TP, TemporalProfile):
            return None
        #self.mColumNames = ['id','coordinate','loaded']
        if role == Qt.DisplayRole:
            if columnName == self.mcnID:
                value = TP.mID
            elif columnName == self.mcnName:
                value = TP.name()
            elif columnName == self.mcnCoordinate:
                value = '{}'.format(TP.mCoordinate)
            elif columnName == self.mcnLoaded:
                nIs, nMax = TP.loadingStatus()
                if nMax > 0:
                    value = '{}/{} ({:0.2f} %)'.format(nIs, nMax, float(nIs) / nMax * 100)
        elif role == Qt.EditRole:
            if columnName == self.mcnName:
                value = TP.name()
        elif role == Qt.ToolTipRole:
            if columnName == self.mcnID:
                value = 'ID Temporal Profile'
            elif columnName == self.mcnName:
                value = TP.name()
            elif columnName == self.mcnCoordinate:
                value = '{}'.format(TP.mCoordinate)
            elif columnName == self.mcnLoaded:
                nIs, nMax = TP.loadingStatus()
                value = '{}'.format(TP.mCoordinate)
        elif role == Qt.UserRole:
            value = TP

        return value

    def flags(self, index):
        if index.isValid():
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable

            cName = self.mColumNames[index.column()]
            if cName == self.mcnName:
                flags = flags | Qt.ItemIsEditable

            return flags
            #return item.qt_flags(index.column())
        return None


    def setData(self, index, value, role=None):
        if role is None or not index.isValid():
            return None

        cName = self.mColumNames[index.column()]
        TP = self.idx2tp(index)
        if isinstance(TP, TemporalProfile):
            if role == Qt.EditRole and cName == self.mcnName:
                if len(value) == 0: #do not accept empty strings
                    return False
                else:
                    TP.setName(value)
                return True

        return False

    def headerData(self, col, orientation, role):
        if Qt is None:
            return None
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                return self.mColumNames[col]
            elif orientation == Qt.Vertical:
                return col
        return None

    def insertTemporalProfiles(self, temporalProfiles, i=None):
        if isinstance(temporalProfiles, TemporalProfile):
            temporalProfiles = [temporalProfiles]

        assert isinstance(temporalProfiles, list)
        for temporalProfile in temporalProfiles:
            assert isinstance(temporalProfile, TemporalProfile)

        if i is None:
            i = len(self.mTemporalProfiles)

        if len(temporalProfiles) > 0:
            self.beginInsertRows(QModelIndex(), i, i + len(temporalProfiles) - 1)
            for temporalProfile in temporalProfiles:
                assert isinstance(temporalProfile, TemporalProfile)
                id = self.nextID
                self.nextID += 1
                temporalProfile.mID = id
                self.mTemporalProfiles.insert(i, temporalProfile)
                self.mTPLookupID[id] = temporalProfile
                self.mTPLookupSpatialPoint[temporalProfile.mCoordinate] = temporalProfile
                i += 1
            self.endInsertRows()

            self.sigTemporalProfilesAdded.emit(temporalProfiles)

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def temporalProfileFromGeometry(self, geometry):
        if geometry in self.mTPLookupSpatialPoint.keys():
            return self.mTPLookupSpatialPoint[geometry]
        else:
            return None

    def temporalProfileFromID(self, id):
        if id in self.mTPLookupID.keys():
            return self.mTPLookupID[id]
        else:
            return None

    def id(self, temporalProfile):
        """
        Returns the id of an TemporalProfile
        :param temporalProfile: TemporalProfile
        :return: id or None, inf temporalProfile is not part of this collections
        """

        for k, tp in self.mTPLookupID.items():
            if tp == temporalProfile:
                return k
        return None

    def fromID(self, id):
        if self.mTPLookupID.has_key(id):
            return self.mTPLookupID[id]
        else:
            return None

    def fromSpatialPoint(self, spatialPoint):
        if self.mTPLookupSpatialPoint.has_key(spatialPoint):
            return self.mTPLookupSpatialPoint[spatialPoint]
        else:
            return None

    def removeTemporalProfiles(self, temporalProfiles):
        """
        Removes temporal profiles from this collection
        :param temporalProfile: TemporalProfile
        """

        if isinstance(temporalProfiles, TemporalProfile):
            temporalProfiles = [temporalProfiles]
        assert isinstance(temporalProfiles, list)

        temporalProfiles = [tp for tp in temporalProfiles if isinstance(tp, TemporalProfile) and tp in self.mTemporalProfiles]

        if len(temporalProfiles) > 0:

            def deleteFromDict(d, value):
                assert isinstance(d, dict)
                if value in d.values():
                    key = d.keys()[d.values().index(value)]
                    d.pop(key)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            for temporalProfile in temporalProfiles:
                assert isinstance(temporalProfile, TemporalProfile)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                idx = self.tp2idx(temporalProfile)
                row = idx.row()
                self.beginRemoveRows(QModelIndex(), row, row)
                self.mTemporalProfiles.remove(temporalProfile)

                deleteFromDict(self.mTPLookupID, temporalProfile)
                deleteFromDict(self.mTPLookupSpatialPoint,  temporalProfile)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
                self.endRemoveRows()
            self.sigTemporalProfilesRemoved.emit(temporalProfiles)

    def connectTimeSeries(self, timeSeries):

        if isinstance(timeSeries, TimeSeries):
            self.TS = timeSeries
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            #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

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def setMaxProfiles(self, n):