Skip to content
Snippets Groups Projects
profilevisualization.py 71.9 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
import os, sys, pickle, datetime
from collections import OrderedDict
from qgis.gui import *
from qgis.core import *
from qgis.core import QgsExpression
from PyQt5.QtCore import *
from PyQt5.QtXml import *
from PyQt5.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
from timeseriesviewer.temporalprofiles import *
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



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 3D')
            self.renderText(5, 10, 'text 2D fixed')
    """
    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 selectedModelIndices(tableView):
    assert isinstance(tableView, QTableView)
    result = {}

    sm = tableView.selectionModel()
    m = tableView.model()
    if isinstance(sm, QItemSelectionModel) and isinstance(m, QAbstractItemModel):
        for idx in sm.selectedIndexes():
            assert isinstance(idx, QModelIndex)
            if idx.row() not in result.keys():
                result[idx.row()] = idx
    return result.values()

Benjamin Jakimow's avatar
Benjamin Jakimow committed

class _SensorPoints(pg.PlotDataItem):
    def __init__(self, *args, **kwds):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        super(_SensorPoints, self).__init__(*args, **kwds)
        # menu creation is deferred because it is expensive and often
        # the user will never see the menu anyway.
        self.menu = None

    def boundingRect(self):
        return super(_SensorPoints,self).boundingRect()
        super(_SensorPoints, self).paint(p, *args)

    # On right-click, raise the context menu
    def mouseClickEvent(self, ev):
        if ev.button() == QtCore.Qt.RightButton:
            if self.raiseContextMenu(ev):
                ev.accept()

    def raiseContextMenu(self, ev):
        menu = self.getContextMenus()

        # Let the scene add on to the end of our context menu
        # (this is optional)
        menu = self.scene().addParentContextMenus(self, menu, ev)

        pos = ev.screenPos()
        menu.popup(QtCore.QPoint(pos.x(), pos.y()))
        return True

    # This method will be called when this item's _children_ want to raise
    # a context menu that includes their parents' menus.
    def getContextMenus(self, event=None):
        if self.menu is None:
            self.menu.setTitle(self.name + " options..")

            green = QAction("Turn green", self.menu)
            green.triggered.connect(self.setGreen)
            self.menu.addAction(green)
            self.menu.green = green

            blue = QAction("Turn blue", self.menu)
            blue.triggered.connect(self.setBlue)
            self.menu.addAction(blue)
            self.menu.green = blue

            alpha = QWidgetAction(self.menu)
            alphaSlider = QSlider()
            alphaSlider.setOrientation(QtCore.Qt.Horizontal)
            alphaSlider.setMaximum(255)
            alphaSlider.setValue(255)
            alphaSlider.valueChanged.connect(self.setAlpha)
            alpha.setDefaultWidget(alphaSlider)
            self.menu.addAction(alpha)
            self.menu.alpha = alpha
            self.menu.alphaSlider = alphaSlider
        return self.menu


Benjamin Jakimow's avatar
Benjamin Jakimow committed

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


Benjamin Jakimow's avatar
Benjamin Jakimow committed
class SensorPixelDataMemoryLayer(QgsVectorLayer):

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

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

        # 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 PlotSettingsModel3D(QAbstractTableModel):

    #sigSensorAdded = pyqtSignal(SensorPlotSettings)
    sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    sigPlotStylesAdded = pyqtSignal(list)
    sigPlotStylesRemoved = pyqtSignal(list)

    def __init__(self, parent=None, *args):

        #assert isinstance(tableView, QTableView)

        super(PlotSettingsModel3D, self).__init__(parent=parent)
        self.mTimeSeries = None
        self.cnScale = 'Scale'
        self.cnOffset = 'Offset'

        self.columNames = [self.cnSensor, self.cnScale, self.cnOffset, self.cnStyle]

        self.mPlotSettings = []
        #assert isinstance(plotWidget, DateTimePlotWidget)

        self.sortColumnIndex = 0
        self.sortOrder = Qt.AscendingOrder


        self.sort(0, Qt.AscendingOrder)

    def connectTimeSeries(self, timeSeries):
        if isinstance(timeSeries, TimeSeries):

            self.mTimeSeries = timeSeries
            self.mTimeSeries.sigSensorAdded.connect(self.createStyle)
            self.mTimeSeries.sigSensorRemoved.connect(self.onSensorRemoved)
            for sensor in self.mTimeSeries.sensors():
                self.onSensorAdded(sensor)


    def hasStyleForSensor(self, sensor):
        assert isinstance(sensor, SensorInstrument)
        for plotStyle in self.mPlotSettings:
            assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
            if plotStyle.sensor() == sensor:
                return True
        return False


    def createStyle(self, sensor):
        if not self.hasStyleForSensor(sensor):
            s = TemporalProfile3DPlotStyle(sensor)
            #use another color for the new sensor
            if len(self) > 0:
                color = self[-1].color()
                s.setColor(nextColor(color))
            self.insertPlotStyles([s])

    def onSensorRemoved(self, sensor):
        assert isinstance(sensor, SensorInstrument)
        self.removePlotStyles([s for s in self.mPlotSettings if s.sensor() == sensor])


    def __len__(self):
        return len(self.mPlotSettings)

    def __iter__(self):
        return iter(self.mPlotSettings)

    def __getitem__(self, slice):
        return self.mPlotSettings[slice]

    def __contains__(self, item):
        return item in self.mPlotSettings


    def columnIndex(self, name):
        return self.columNames.index(name)


    def insertPlotStyles(self, plotStyles, i=None):
        """
        Inserts PlotStyle
        :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
        :param i: index to insert, defaults to the last list position
        """
        if isinstance(plotStyles, TemporalProfile3DPlotStyle):
            plotStyles = [plotStyles]
        assert isinstance(plotStyles, list)
        for plotStyle in plotStyles:
            assert isinstance(plotStyle, TemporalProfile3DPlotStyle)

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

        if len(plotStyles) > 0:
            self.beginInsertRows(QModelIndex(), i, i + len(plotStyles)-1)
            for j, plotStyle in enumerate(plotStyles):
                assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                self.mPlotSettings.insert(i+j, plotStyle)
            self.endInsertRows()
            self.sigPlotStylesAdded.emit(plotStyles)

    def removePlotStyles(self, plotStyles):
        """
        Removes PlotStyle instances
        :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
        """
        if isinstance(plotStyles, TemporalProfile3DPlotStyle):
            plotStyles = [plotStyles]
        assert isinstance(plotStyles, list)

        if len(plotStyles) > 0:
            for plotStyle in plotStyles:
                assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                if plotStyle in self.mPlotSettings:
                    idx = self.plotStyle2idx(plotStyle)
                    self.beginRemoveRows(QModelIndex(), idx.row(),idx.row())
                    self.mPlotSettings.remove(plotStyle)
                    self.endRemoveRows()
            self.sigPlotStylesRemoved.emit(plotStyles)

    def sort(self, col, order):
        if self.rowCount() == 0:
            return


        colName = self.columnames[col]
        r = order != Qt.AscendingOrder

        #self.beginMoveRows(idxSrc,

        if colName == self.cnSensor:
            self.mPlotSettings.sort(key = lambda sv:sv.sensor().name(), reverse=r)

    def rowCount(self, parent = QModelIndex()):
        return len(self.mPlotSettings)


    def removeRows(self, row, count , parent = QModelIndex()):

        self.beginRemoveRows(parent, row, row + count-1)

        toRemove = self.mPlotSettings[row:row + count]

        for tsd in toRemove:
            self.mPlotSettings.remove(tsd)

        self.endRemoveRows()

    def plotStyle2idx(self, plotStyle):

        assert isinstance(plotStyle, TemporalProfile3DPlotStyle)

        if plotStyle in self.mPlotSettings:
            i = self.mPlotSettings.index(plotStyle)
            return self.createIndex(i, 0)
        else:
            return QModelIndex()

    def idx2plotStyle(self, index):

        if index.isValid() and index.row() < self.rowCount():
            return self.mPlotSettings[index.row()]

        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()]
        plotStyle = self.idx2plotStyle(index)
        if isinstance(plotStyle, TemporalProfile3DPlotStyle):
            sensor = plotStyle.sensor()
            #print(('data', columnName, role))
            if role == Qt.DisplayRole:
                if columnName == self.cnSensor:
                    if isinstance(sensor, SensorInstrument):
                        value = sensor.name()
                    else:
                        value = '<Select Sensor>'
                elif columnName == self.cnScale:
                    value = plotStyle.mScale
                elif columnName == self.cnOffset:
                    value = plotStyle.mOffset
            if role == Qt.EditRole:
                if columnName == self.cnScale:
                    value = plotStyle.mScale
                elif columnName == self.cnOffset:
                    value = plotStyle.mOffset

            elif role == Qt.CheckStateRole:
                if columnName == self.cnSensor:
                    value = Qt.Checked if plotStyle.isVisible() else Qt.Unchecked

            elif role == Qt.UserRole:
                value = plotStyle
                if columnName == self.cnSensor:
                    value = plotStyle.sensor()
                elif columnName == self.cnStyle:
                    value = plotStyle
                else:
                    value = plotStyle
        #print(('get data',value))
        return value

    def setData(self, index, value, role=None):
        if role is None or not index.isValid():
            return False
        #print(('Set data', index.row(), index.column(), value, role))
        columnName = self.columNames[index.column()]

        if value is None:
            return False

        result = False
        plotStyle = self.idx2plotStyle(index)
        if isinstance(plotStyle, TemporalProfile3DPlotStyle):
            if role in [Qt.DisplayRole]:
                if columnName == self.cnScale and isinstance(value, float):
                    plotStyle.setScaling(value, plotStyle.mOffset)
                    result = True
                elif columnName == self.cnOffset and isinstance(value, float):
                    plotStyle.setScaling(plotStyle.mScale, value)
                    result = True
                elif columnName == self.cnStyle:
                    if isinstance(value, PlotStyle):
                        plotStyle.copyFrom(value)
                        result = True
                    elif isinstance(value, QColor):
                        plotStyle.setColor(value)
                        result = True

            if role == Qt.CheckStateRole:
                if columnName == self.cnSensor:
                    plotStyle.setVisibility(value == Qt.Checked)
                    result = True

            if role == Qt.EditRole:
                if columnName == self.cnScale:
                    plotStyle.setScaling(value, plotStyle.mOffset)
                    result = True
                elif columnName == self.cnOffset:
                    plotStyle.setScaling(plotStyle.mScale, value)
                    result = True
                elif columnName == self.cnStyle:
                    if isinstance(value, QColor):
                        plotStyle.setColor(value)
                        result = True
                    if isinstance(value, TemporalProfile3DPlotStyle):
                        plotStyle.copyFrom(value)
                        result = True

        return result


    def flags(self, index):
        if index.isValid():
            columnName = self.columNames[index.column()]
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
            if columnName in [self.cnSensor]:
                flags = flags | Qt.ItemIsUserCheckable
            if columnName in [self.cnScale, self.cnOffset, self.cnStyle]: #allow check state
                flags = flags | Qt.ItemIsEditable
            return flags
            #return item.qt_flags(index.column())
        return Qt.NoItemFlags

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


class PlotSettingsModel2D(QAbstractTableModel):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    #sigSensorAdded = pyqtSignal(SensorPlotSettings)
    sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    sigDataChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    sigPlotStylesAdded = pyqtSignal(list)
    sigPlotStylesRemoved = pyqtSignal(list)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __init__(self, temporalProfileCollection, plotWidget, parent=None, *args):

        #assert isinstance(tableView, QTableView)

        super(PlotSettingsModel2D, self).__init__(parent=parent)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(temporalProfileCollection, TemporalProfileCollection)

        self.cnID = 'ID'
        self.cnExpression = LABEL_DN
        self.cnTemporalProfile = 'Coordinate'
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.columNames = [self.cnTemporalProfile, self.cnSensor, self.cnStyle, self.cnExpression]

        self.mPlotSettings = []
        #assert isinstance(plotWidget, DateTimePlotWidget)
        self.mPlotWidget = plotWidget
        self.sortColumnIndex = 0
        self.sortOrder = Qt.AscendingOrder
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.tpCollection = temporalProfileCollection
        #self.tpCollection.sigTemporalProfilesRemoved.connect(lambda removedTPs : self.removePlotStyles([p for p in self.mPlotSettings if p.temporalProfile() in removedTPs]))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(self.tpCollection.TS, TimeSeries)
        #self.tpCollection.TS.sigSensorAdded.connect(self.addPlotItem)
        #self.tpCollection.TS.sigSensorRemoved.connect(self.removeSensor)

        self.sort(0, Qt.AscendingOrder)
        self.dataChanged.connect(self.signaler)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __len__(self):
        return len(self.mPlotSettings)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __iter__(self):
        return iter(self.mPlotSettings)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __getitem__(self, slice):
        return self.mPlotSettings[slice]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __contains__(self, item):
        return item in self.mPlotSettings
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def testSlot(self, *args):
        print(('TESTSLOT', args))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def columnIndex(self, name):
        return self.columNames.index(name)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def signaler(self, idxUL, idxLR):
        if idxUL.isValid():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            plotStyle = self.idx2plotStyle(idxUL)
            cname = self.columNames[idxUL.column()]
            if cname in [self.cnSensor,self.cnStyle]:
                self.sigVisibilityChanged.emit(plotStyle)
            if cname in [self.cnExpression]:
                self.sigDataChanged.emit(plotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def requiredBandsIndices(self, sensor):
        """
        Returns the band indices required to calculate the values for
        the different PlotStyle expressions making use of sensor
        :param sensor: SensorInstrument for which the band indices are to be returned.
        :return: [list-of-band-indices]
        """
        bandIndices = set()
        assert isinstance(sensor, SensorInstrument)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for p in [p for p in self.mPlotSettings if p.sensor() == sensor]:
            assert isinstance(p, TemporalProfile2DPlotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            expression = p.expression()
            #remove leading & tailing "
            bandKeys = regBandKey.findall(expression)
            for bandIndex in [bandKey2bandIndex(key) for key in bandKeys]:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                bandIndices.add(bandIndex)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return bandIndices
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def insertPlotStyles(self, plotStyles, i=None):
        """
        Inserts PlotStyle
        :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
        :param i: index to insert, defaults to the last list position
        """
        if isinstance(plotStyles, TemporalProfile2DPlotStyle):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            plotStyles = [plotStyles]
        assert isinstance(plotStyles, list)
        for plotStyle in plotStyles:
            assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if i is None:
            i = len(self.mPlotSettings)
        if len(plotStyles) > 0:
            self.beginInsertRows(QModelIndex(), i, i + len(plotStyles)-1)
            for j, plotStyle in enumerate(plotStyles):
                assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
                self.mPlotSettings.insert(i+j, plotStyle)
            self.endInsertRows()
            self.sigPlotStylesAdded.emit(plotStyles)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def removePlotStyles(self, plotStyles):
        """
        Removes PlotStyle instances
        :param plotStyles: TemporalProfilePlotStyle | [list-of-TemporalProfilePlotStyle]
        """
        if isinstance(plotStyles, PlotStyle):
            plotStyles = [plotStyles]
        assert isinstance(plotStyles, list)

        if len(plotStyles) > 0:
            for plotStyle in plotStyles:
                assert isinstance(plotStyle, PlotStyle)
                if plotStyle in self.mPlotSettings:
                    idx = self.plotStyle2idx(plotStyle)
                    self.beginRemoveRows(QModelIndex(), idx.row(),idx.row())
                    self.mPlotSettings.remove(plotStyle)
                    self.endRemoveRows()
                if isinstance(plotStyle, TemporalProfile2DPlotStyle):
                    for pi in plotStyle.mPlotItems:
                        self.mPlotWidget.getPlotItem().removeItem(pi)
            self.sigPlotStylesRemoved.emit(plotStyles)

    def sort(self, col, order):
        if self.rowCount() == 0:
            return


        colName = self.columnames[col]
        r = order != Qt.AscendingOrder

        #self.beginMoveRows(idxSrc,

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if colName == self.cnSensor:
            self.mPlotSettings.sort(key = lambda sv:sv.sensor().name(), reverse=r)
        elif colName == self.cnExpression:
            self.mPlotSettings.sort(key=lambda sv: sv.expression(), reverse=r)
        elif colName == self.cnStyle:
            self.mPlotSettings.sort(key=lambda sv: sv.color, reverse=r)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def rowCount(self, parent = QModelIndex()):
        return len(self.mPlotSettings)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def removeRows(self, row, count , parent = QModelIndex()):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.beginRemoveRows(parent, row, row + count-1)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        toRemove = self.mPlotSettings[row:row + count]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.mPlotSettings.remove(tsd)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def plotStyle2idx(self, plotStyle):
        assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if plotStyle in self.mPlotSettings:
            i = self.mPlotSettings.index(plotStyle)
            return self.createIndex(i, 0)
        else:
            return QModelIndex()

    def idx2plotStyle(self, index):

        if index.isValid() and index.row() < self.rowCount():
            return self.mPlotSettings[index.row()]

        return None

    def columnCount(self, parent = QModelIndex()):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return len(self.columNames)

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

        value = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        columnName = self.columNames[index.column()]
        plotStyle = self.idx2plotStyle(index)
        if isinstance(plotStyle, TemporalProfile2DPlotStyle):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            sensor = plotStyle.sensor()
            #print(('data', columnName, role))
            if role == Qt.DisplayRole:
                if columnName == self.cnSensor:
                    if isinstance(sensor, SensorInstrument):
                        value = sensor.name()
                    else:
                        value = '<Select Sensor>'
                elif columnName == self.cnExpression:
                    value = plotStyle.expression()
                elif columnName == self.cnTemporalProfile:
                    value = plotStyle.temporalProfile().name()

            #elif role == Qt.DecorationRole:
            #    if columnName == self.cnStyle:
            #        value = plotStyle.createIcon(QSize(96,96))

            elif role == Qt.CheckStateRole:
                if columnName == self.cnTemporalProfile:
                    value = Qt.Checked if plotStyle.isVisible() else Qt.Unchecked



            elif role == Qt.UserRole:
                value = plotStyle
                if columnName == self.cnSensor:
                    value = plotStyle.sensor()
                elif columnName == self.cnExpression:
                    value = plotStyle.expression()
                elif columnName == self.cnStyle:
                    value = plotStyle
                elif columnName == self.cnTemporalProfile:
                    value == plotStyle.temporalProfile()
                else:
                    value = plotStyle
        #print(('get data',value))
        return value

    def setData(self, index, value, role=None):
        if role is None or not index.isValid():
            return False
        #print(('Set data', index.row(), index.column(), value, role))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        columnName = self.columNames[index.column()]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        result = False
        plotStyle = self.idx2plotStyle(index)
        if isinstance(plotStyle, TemporalProfile2DPlotStyle):
            if role in [Qt.DisplayRole]:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if columnName == self.cnExpression:
                    plotStyle.setExpression(value)
                    result = True
                elif columnName == self.cnStyle:
                    if isinstance(value, PlotStyle):
                        plotStyle.copyFrom(value)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                        result = True

            if role == Qt.CheckStateRole:
                if columnName == self.cnTemporalProfile:
                    plotStyle.setVisibility(value == Qt.Checked)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if role == Qt.EditRole:
                if columnName == self.cnExpression:
                    plotStyle.setExpression(value)
                    result = True
                elif columnName == self.cnStyle:
                    plotStyle.copyFrom(value)
                    result = True
                elif columnName == self.cnSensor:
                    plotStyle.setSensor(value)
                    result = True
                elif columnName == self.cnTemporalProfile:
                    plotStyle.setTemporalProfile(value)
                    result = True
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.savePlotSettings(plotStyle, index='DEFAULT')
            self.dataChanged.emit(index, index)

        return result

    def savePlotSettings(self, sensorPlotSettings, index='DEFAULT'):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return
        #todo
        assert isinstance(sensorPlotSettings, TemporalProfile2DPlotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #todo: avoid dumps
        id = 'SPS.{}.{}'.format(index, sensorPlotSettings.sensor().id())
        d = pickle.dumps(sensorPlotSettings)
        SETTINGS.setValue(id, d)

    def restorePlotSettings(self, sensor, index='DEFAULT'):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return None

        #todo
        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, TemporalProfile2DPlotStyle):
            return sensorPlotSettings
    def flags(self, index):
        if index.isValid():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            columnName = self.columNames[index.column()]
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if columnName in [self.cnTemporalProfile]:
                flags = flags | Qt.ItemIsUserCheckable
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if columnName in [self.cnTemporalProfile, self.cnSensor, self.cnExpression, self.cnStyle]: #allow check state
                flags = flags | Qt.ItemIsEditable
            return flags
            #return item.qt_flags(index.column())
        return Qt.NoItemFlags

    def headerData(self, col, orientation, role):
        if Qt is None:
            return None
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            return self.columNames[col]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return col
        return None



Benjamin Jakimow's avatar
Benjamin Jakimow committed
class ProfileViewDockUI(QgsDockWidget, loadUI('profileviewdock.ui')):
    def __init__(self, parent=None):
        super(ProfileViewDockUI, self).__init__(parent)
        self.setupUi(self)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #self.line.setVisible(False)
        #self.listWidget.setVisible(False)
        self.baseTitle = self.windowTitle()
        self.stackedWidget.currentChanged.connect(self.onStackPageChanged)
        self.stackedWidget.setCurrentWidget(self.page2D)
        self.plotWidget3D = None
            l = self.frame3DPlot.layout()
            #from pyqtgraph.opengl import GLViewWidget
            #self.plotWidget3D = GLViewWidget(parent=self.page3D)
            self.plotWidget3D = ViewWidget3D(parent=self.frame3DPlot)
            self.plotWidget3D.setObjectName('plotWidget3D')
            size = self.labelDummy3D.size()
            self.plotWidget3D.setSizePolicy(self.labelDummy3D.sizePolicy())
            self.labelDummy3D.setVisible(False)
            l.removeWidget(self.labelDummy3D)
            self.plotWidget3D.setBaseSize(size)
            self.splitter3D.setSizes([100, 100])
        #pi = self.plotWidget2D.plotItem
        #ax = DateAxis(orientation='bottom', showValues=True)
        #pi.layout.addItem(ax, 3,2)
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(100)
        self.progressBar.setValue(0)
        self.progressInfo.setText('')
        self.pxViewModel2D = None
        self.pxViewModel3D = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        self.tableView2DProfiles.setSortingEnabled(True)
        self.tableViewTemporalProfiles.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        self.tableViewTemporalProfiles.setSortingEnabled(True)
        self.menuTPSaveOptions = QMenu()
        self.menuTPSaveOptions.addAction(self.actionSaveTPCoordinates)
        self.menuTPSaveOptions.addAction(self.actionSaveTPCSV)
        self.menuTPSaveOptions.addAction(self.actionSaveTPVector)
        self.btnSaveTemporalProfiles.setMenu(self.menuTPSaveOptions)

    def onStackPageChanged(self, i):
        w = self.stackedWidget.currentWidget()
        title = self.baseTitle
        if w == self.page2D:
            title = '{} | 2D'.format(title)
            title = '{} | 3D'.format(title)
            title = '{} | Coordinates'.format(title)
NEXT_COLOR_HUE_DELTA_CON = 10
NEXT_COLOR_HUE_DELTA_CAT = 100
def nextColor(color, mode='cat'):
    """
    Reuturns another color
    :param color:
    :param mode:
    :return:
    """
    assert mode in ['cat','con']
    assert isinstance(color, QColor)
    hue, sat, value, alpha = color.getHsl()
    if mode == 'cat':
        hue += NEXT_COLOR_HUE_DELTA_CAT
    elif mode == 'con':
        hue += NEXT_COLOR_HUE_DELTA_CON
    if sat == 0:
        sat = 255
        value = 128
        alpha = 255
    return QColor.fromHsl(hue, sat, value, alpha)
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(ui, ProfileViewDockUI), 'arg ui of type: {} {}'.format(type(ui), str(ui))
        if DEBUG:
            import timeseriesviewer.pixelloader
            timeseriesviewer.pixelloader.DEBUG = True

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.TS = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        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.tableView2DProfiles = ui.tableView2DProfiles
        self.tableView2DProfiles.setSortingEnabled(False)

        self.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        self.plotSettingsModel3D = PlotSettingsModel3D()
        #self.plotSettingsModel3D.sigPlotStylesRemoved.connect(self.updatePlot3D)
        #self.plotSettingsModel3D.sigPlotStylesAdded.connect(self.updatePlot3D)
        #self.plotSettingsModel3D.sigPlotStylesAdded.connect(self.updatePlot3D)
        self.plotSettingsModel3D.rowsInserted.connect(self.onRowsInserted3D)
        self.ui.tableView3DProfiles.setModel(self.plotSettingsModel3D)
        self.ui.tableView3DProfiles.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        self.delegateTableView3D = PlotSettingsModel3DWidgetDelegate(self.ui.tableView3DProfiles)
        self.delegateTableView3D.setItemDelegates(self.ui.tableView3DProfiles)
        # self.mSelectionModel.currentChanged.connect(self.onCurrentSelectionChanged)

        self.plot2D = ui.plotWidget2D
        self.plot2D.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
        self.plot3D = ui.plotWidget3D
        self.reset3DCamera()

        ## Add a grid to the view
        if OPENGL_AVAILABLE:
            import pyqtgraph.opengl as gl
            self.glGridItem = gl.GLGridItem()
            self.glGridItem.setDepthValue(10)  # draw grid after surfaces since they may be translucent
            self.glPlotDataItems = [self.glGridItem]
            self.plot3D.addItem(self.glGridItem)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.tpCollection = TemporalProfileCollection()
        self.tpCollectionListModel = TemporalProfileCollectionListModel(self.tpCollection)

        self.ui.tableViewTemporalProfiles.setModel(self.tpCollection)
        self.ui.tableViewTemporalProfiles.selectionModel().selectionChanged.connect(self.onTemporalProfileSelectionChanged)
        self.ui.tableViewTemporalProfiles.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.cbTemporalProfile3D.setModel(self.tpCollectionListModel)
        #self.pxCollection.sigPixelAdded.connect(self.requestUpdate)
        #self.pxCollection.sigPixelRemoved.connect(self.clear)
        self.plotSettingsModel2D = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.pixelLoader.sigLoadingStarted.connect(self.clear)
        self.pixelLoader.sigLoadingFinished.connect(lambda : self.plot2D.enableAutoRange('x', False))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed

        # 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.onDataUpdate)
        self.updateTimer.start(5000)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        self.sigMoveToDate.connect(self.onMoveToDate)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.initActions()



    def selected2DPlotStyles(self):
        result = []

        m = self.ui.tableView2DProfiles.model()
        for idx in selectedModelIndices(self.ui.tableView2DProfiles):
            result.append(m.idx2plotStyle(idx))
        return result

    def selectedTemporalProfiles(self):
        result = []
        m = self.ui.tableViewTemporalProfiles.model()
        for idx in selectedModelIndices(self.ui.tableViewTemporalProfiles):
            result.append(m.idx2tp(idx))
        return result

    def removePlotStyles(self, plotStyles):
        m = self.ui.tableView2DProfiles.model()
        if isinstance(m, PlotSettingsModel2D):
            m.removePlotStyles(plotStyles)

    def removeTemporalProfiles(self, temporalProfiles):
        m = self.ui.tableViewTemporalProfiles.model()
        if isinstance(m, TemporalProfileCollection):
            m.removeTemporalProfiles(temporalProfiles)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def createNewPlotStyle(self):
        l = len(self.tpCollection)
        if l > 0:
            temporalProfile = self.tpCollection[0]
            plotStyle = TemporalProfile2DPlotStyle(temporalProfile)
            plotStyle.sigExpressionUpdated.connect(self.updatePlot2D)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            sensors = self.TS.Sensors.keys()
            if len(sensors) > 0:
                plotStyle.setSensor(sensors[0])

            if len(self.plotSettingsModel2D) > 0:
                lastStyle = self.plotSettingsModel2D[-1]
                assert isinstance(lastStyle, TemporalProfile2DPlotStyle)
                markerColor = nextColor(lastStyle.markerBrush.color())
                plotStyle.markerBrush.setColor(markerColor)
            self.plotSettingsModel2D.insertPlotStyles([plotStyle])
            pdi = plotStyle.createPlotItem(self.plot2D)

            assert isinstance(pdi, TemporalProfilePlotDataItem)
            pdi.sigClicked.connect(self.onProfileClicked2D)
            pdi.sigPointsClicked.connect(self.onPointsClicked2D)
            self.plot2D.getPlotItem().addItem(pdi)
            #self.plot2D.getPlotItem().addItem(pg.PlotDataItem(x=[1, 2, 3], y=[1, 2, 3]))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            #plotItem.addDataItem(pdi)
            #plotItem.plot().sigPlotChanged.emit(plotItem)
            self.updatePlot2D()

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def onProfileClicked2D(self, pdi):
        if isinstance(pdi, TemporalProfilePlotDataItem):
            sensor = pdi.mPlotStyle.sensor()
            tp = pdi.mPlotStyle.temporalProfile()
            if isinstance(tp, TemporalProfile) and isinstance(sensor, SensorInstrument):
                info = ['Sensor:{}'.format(sensor.name()),
                        'Coordinate:{}, {}'.format(tp.mCoordinate.x(), tp.mCoordinate.y())]
                self.ui.tbInfo2D.setPlainText('\n'.join(info))


    def onPointsClicked2D(self, pdi, spottedItems):
        if isinstance(pdi, TemporalProfilePlotDataItem) and isinstance(spottedItems, list):
            sensor = pdi.mPlotStyle.sensor()
            tp = pdi.mPlotStyle.temporalProfile()
            if isinstance(tp, TemporalProfile) and isinstance(sensor, SensorInstrument):

                info = ['Sensor: {}'.format(sensor.name()),
                        'Coordinate: {}, {}'.format(tp.mCoordinate.x(), tp.mCoordinate.y())]

                for item in spottedItems:
                    pos = item.pos()
                    x = pos.x()
                    y = pos.y()
                    date = num2date(x)
                    info.append('Date: {}\nValue: {}'.format(date, y))

                self.ui.tbInfo2D.setPlainText('\n'.join(info))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def onTemporalProfileSelectionChanged(self, selected, deselected):
        nSelected = len(selected)
        self.ui.actionRemoveTemporalProfile.setEnabled(nSelected > 0)
        self.ui.btnSaveTemporalProfiles.setEnabled(nSelected > 0)

    def onPlot2DSelectionChanged(self, selected, deselected):

        self.ui.actionRemoveView.setEnabled(len(selected) > 0)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def initActions(self):

        self.ui.actionRemoveView.setEnabled(False)
        self.ui.actionRemoveTemporalProfile.setEnabled(False)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.btnAddView.setDefaultAction(self.ui.actionAddView)
        self.ui.btnRemoveView.setDefaultAction(self.ui.actionRemoveView)
        self.ui.btnRefresh2D.setDefaultAction(self.ui.actionRefresh2D)
        self.ui.btnRefresh3D.setDefaultAction(self.ui.actionRefresh3D)
        self.ui.btnRemoveTemporalProfile.setDefaultAction(self.ui.actionRemoveTemporalProfile)
        self.ui.btnReset3DCamera.setDefaultAction(self.ui.actionReset3DCamera)

        self.ui.actionRefresh2D.triggered.connect(self.updatePlot2D)
        self.ui.actionRefresh3D.triggered.connect(self.updatePlot3D)
        self.ui.btnLoadProfile1.setDefaultAction(self.ui.actionLoadProfileRequest)
        self.ui.btnLoadProfile2.setDefaultAction(self.ui.actionLoadProfileRequest)
        self.ui.btnLoadProfile3.setDefaultAction(self.ui.actionLoadProfileRequest)


Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.actionAddView.triggered.connect(self.createNewPlotStyle)
        self.ui.actionRemoveView.triggered.connect(lambda:self.removePlotStyles(self.selected2DPlotStyles()))
        self.ui.actionRemoveTemporalProfile.triggered.connect(lambda :self.removeTemporalProfiles(self.selectedTemporalProfiles()))
        self.ui.actionReset3DCamera.triggered.connect(self.reset3DCamera)
        self.tpCollection.sigMaxProfilesChanged.connect(self.ui.sbMaxTP.setValue)
        self.ui.sbMaxTP.valueChanged.connect(self.tpCollection.setMaxProfiles)


        from timeseriesviewer.temporalprofiles import saveTemporalProfiles
        DEF_PATH = None

        self.ui.actionSaveTPCoordinates.triggered.connect(
            lambda: saveTemporalProfiles(self.tpCollection[:],
                    QFileDialog.getSaveFileName(
                        self.ui, 'Save Temporal Profile Coordinates',
                        DEF_PATH, 'ESRI Shapefile (*.shp);;Geopackage (*.gpkg);;Textfile (*.csv *.txt)'
                    ), mode='coordinate'
            )
        )

        self.ui.actionSaveTPCSV.triggered.connect(
            lambda: saveTemporalProfiles(self.tpCollection[:],
                                         QFileDialog.getSaveFileName(
                                             self.ui, 'Save Temporal Profiles to Text File.',
                                             DEF_PATH,
                                             'Textfile (*.csv *.txt)'
                                         ), mode ='all'
                                         )
        )


        self.ui.actionSaveTPVector.triggered.connect(
            lambda: saveTemporalProfiles(self.tpCollection[:],
                                         QFileDialog.getSaveFileName(
                                             self.ui, 'Save Temporal Profiles to Vector File.',
                                             DEF_PATH,
                                             'ESRI Shapefile (*.shp);;Geopackage (*.gpkg)'
                                         ), mode = 'all'
                                         )
        )
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #todo: self.ui.actionRemoveView.triggered.connect(self.plotSettingsModel.createPlotStyle)

    def reset3DCamera(self, *args):

        if OPENGL_AVAILABLE:
            self.plot3D.setCameraPosition((0,0,0), distance=10, elevation=10)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def setTimeSeries(self, TS):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        assert isinstance(TS, TimeSeries)
        self.TS = TS

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.tpCollection.connectTimeSeries(self.TS)

        self.plotSettingsModel2D = PlotSettingsModel2D(self.tpCollection, self.plot2D, parent=self)
        self.plotSettingsModel2D.sigVisibilityChanged.connect(self.setVisibility)
        self.plotSettingsModel2D.sigDataChanged.connect(self.requestUpdate)
        self.plotSettingsModel2D.rowsInserted.connect(self.onRowsInserted2D)
        self.plotSettingsModel3D.connectTimeSeries(self.TS)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        # self.plotSettingsModel.modelReset.connect(self.updatePersistantWidgets)
        self.tableView2DProfiles.setModel(self.plotSettingsModel2D)
        #self.tableView2DProfilesSelectionModel = QItemSelectionModel(self.mModel)
        self.tableView2DProfiles.selectionModel().selectionChanged.connect(self.onPlot2DSelectionChanged)
        #self.tableView2DProfilesSelectionModel.selectionChanged.connect(self.onPlot2DSelectionChanged)
        #self.tableView2DProfilesSelectionModel.setSelectionModel(self.mSelectionModel)
        self.delegateTableView2D = PlotSettingsModel2DWidgetDelegate(self.tableView2DProfiles, self.TS, self.tpCollectionListModel)
        self.delegateTableView2D.setItemDelegates(self.tableView2DProfiles)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    sigMoveToTSD = pyqtSignal(TimeSeriesDatum)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    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])
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def onPixelLoaded(self, nDone, nMax, d):
        self.ui.progressBar.setValue(nDone)
        self.ui.progressBar.setMaximum(nMax)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(d, PixelLoaderTask)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        bn = os.path.basename(d.sourcePath)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if d.success():
            t = 'Last loaded from {}.'.format(bn)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.tpCollection.addPixelLoaderResult(d)
            self.updateRequested = True
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        else:
            t = 'Failed loading from {}.'.format(bn)
            if d.info and d.info != '':
                t += '({})'.format(d.info)
        # QgsApplication.processEvents()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.progressInfo.setText(t)

    def requestUpdate(self, *args):
        self.updateRequested = True
        #next time

    def onRowsInserted2D(self, parent, start, end):
        model = self.tableView2DProfiles.model()
        if isinstance(model, PlotSettingsModel2D):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            colExpression = model.columnIndex(model.cnExpression)
            colStyle = model.columnIndex(model.cnStyle)
            while start <= end:
                idxExpr = model.createIndex(start, colExpression)
                idxStyle = model.createIndex(start, colStyle)
                self.tableView2DProfiles.openPersistentEditor(idxExpr)
                self.tableView2DProfiles.openPersistentEditor(idxStyle)
                start += 1
                #self.TV.openPersistentEditor(model.createIndex(start, colStyle))
            s = ""

    def onRowsInserted3D(self, parent, start, end):
        model = self.ui.tableView3DProfiles.model()
        if isinstance(model, PlotSettingsModel3D):
            colStyle = model.columnIndex(model.cnStyle)
            while start <= end:
                idxStyle = model.createIndex(start, colStyle)
                self.ui.tableView3DProfiles.openPersistentEditor(idxStyle)
                start += 1

    def onObservationClicked(self, plotDataItem, points):
        for p in points:
            tsd = p.data()
            #print(tsd)

    def clear(self):
        #first remove from pixelCollection
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.tpCollection.prune()

        return

        self.plotData2D.clear()
        self.plotData3D.clear()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        pi = self.plot2D.getPlotItem()
        plotItems = pi.listDataItems()
        for p in plotItems:
            p.clear()
            p.update()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if len(self.TS) > 0:
            rng = [self.TS[0].date, self.TS[-1].date]
            rng = [date2num(d) for d in rng]
            self.plot2D.getPlotItem().setRange(xRange=rng)
        #QApplication.processEvents()
    def loadCoordinate(self, spatialPoints=None, LUT_bandIndices=None):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        """
        Loads a temporal profile for a single or multiple geometries.
        :param spatialPoints: SpatialPoint | [list-of-SpatialPoints]
        """
        if not isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
            return False

        if not self.pixelLoader.isReadyToLoad():
            return False
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        assert isinstance(self.TS, TimeSeries)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #Get or create the TimeSeriesProfiles which will store the loaded values

        tasks = []
        TPs = []
        theGeometries = []
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        # Define a which (new) bands need to be loaded for each sensor
        if LUT_bandIndices is None:
            LUT_bandIndices = dict()
            for sensor in self.TS.Sensors:
                LUT_bandIndices[sensor] = self.plotSettingsModel2D.requiredBandsIndices(sensor)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        assert isinstance(LUT_bandIndices, dict)
        for sensor in self.TS.Sensors:
            assert sensor in LUT_bandIndices.keys()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        #update new / existing points
        if isinstance(spatialPoints, SpatialPoint):
            spatialPoints = [spatialPoints]

        for spatialPoint in spatialPoints:
            assert isinstance(spatialPoint, SpatialPoint)
            TP = self.tpCollection.fromSpatialPoint(spatialPoint)
            if not isinstance(TP, TemporalProfile):
                TP = TemporalProfile(self.TS, spatialPoint)
                self.tpCollection.insertTemporalProfiles(TP, i=0)

                if len(self.tpCollection) == 1:
                    if len(self.plotSettingsModel2D) == 0:
                        self.createNewPlotStyle()

                    if len(self.plotSettingsModel3D) == 0:
                        #todo: individual 3D style
                        pass

Benjamin Jakimow's avatar
Benjamin Jakimow committed
            TPs.append(TP)
            theGeometries.append(TP.mCoordinate)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        TP_ids = [TP.mID for TP in TPs]
        #each TSD is a Task
        #a Task defines which bands are to be loaded
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            #do not load from invisible TSDs
            if not tsd.isVisible():
                continue

            #which bands do we need to load?
            requiredIndices = set(LUT_bandIndices[tsd.sensor])
            if len(requiredIndices) == 0:
                continue
            else:
                s = ""

            missingIndices = set()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            for TP in TPs:
                assert isinstance(TP, TemporalProfile)
                need2load = TP.missingBandIndices(tsd, requiredIndices=requiredIndices)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                missingIndices = missingIndices.union(need2load)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            missingIndices = sorted(list(missingIndices))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if len(missingIndices) > 0:
                task = PixelLoaderTask(tsd.pathImg, theGeometries,
                                       bandIndices=missingIndices,
                                       temporalProfileIDs=TP_ids)
                tasks.append(task)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if len(tasks) > 0:
            aGoodDefault = 2 if len(self.TS) > 25 else 1

            self.pixelLoader.setNumberOfProcesses(SETTINGS.value('profileloader_threads', aGoodDefault))
            if DEBUG:
                print('Start loading for {} geometries from {} sources...'.format(
                    len(theGeometries), len(tasks)
                ))
            self.pixelLoader.startLoading(tasks)

        else:
            if DEBUG:
                print('Data for geometries already loaded')
    def setVisibility(self, sensorPlotStyle):
        assert isinstance(sensorPlotStyle, TemporalProfile2DPlotStyle)
        self.setVisibility2D(sensorPlotStyle)
    def setVisibility2D(self, sensorPlotStyle):
    def addData(self, sensorView = None):
            for sv in self.plotSettingsModel2D.items:
            assert isinstance(sensorView, TemporalProfile2DPlotStyle)
    @QtCore.pyqtSlot()
        for plotSetting in self.plotSettingsModel2D:
            assert isinstance(plotSetting, TemporalProfile2DPlotStyle)
            if plotSetting.temporalProfile().updated():
                for pdi in plotSetting.mPlotItems:
                    assert isinstance(pdi, TemporalProfilePlotDataItem)
                    pdi.updateDataAndStyle()

        for i in self.plot2D.getPlotItem().dataItems:
            i.updateItems()


        notInit = [0, 1] == self.plot2D.getPlotItem().getAxis('bottom').range
        if notInit:
            for plotSetting in self.plotSettingsModel2D:
                assert isinstance(plotSetting, TemporalProfile2DPlotStyle)
                for pdi in plotSetting.mPlotItems:
                    assert isinstance(pdi, TemporalProfilePlotDataItem)
                    if pdi.xData.ndim == 0 or pdi.xData.shape[0] == 0:
                        continue
                    if x0 is None:
                        x0 = pdi.xData.min()
                        x1 = pdi.xData.max()
                    else:
                        x0 = min(pdi.xData.min(), x0)
                        x1 = max(pdi.xData.max(), x1)

            if x0 is not None:
                self.plot2D.getPlotItem().setXRange(x0, x1)
                #self.plot2D.xAxisInitialized = True

    @QtCore.pyqtSlot()
    def updatePlot3D(self):
        if OPENGL_AVAILABLE:
            from pyqtgraph.opengl import GLViewWidget
            import pyqtgraph.opengl as gl
            assert isinstance(self.plot3D, GLViewWidget)
            w = self.plot3D

            #we need the data from all bands



            del self.glPlotDataItems[:]
            for i in w.items:
                w.removeItem(i)

            idx = self.ui.cbTemporalProfile3D.currentIndex()
            if idx >= 0:
                tp = self.ui.cbTemporalProfile3D.itemData(idx, role=Qt.UserRole)
                assert isinstance(tp, TemporalProfile)

                #1. ensure that data from all bands will be loaded
                LUT_bandIndices = dict()
                for sensor in self.TS.sensors():
                    assert isinstance(sensor, SensorInstrument)
                    LUT_bandIndices[sensor] = list(range(sensor.nb))

                self.loadCoordinate(tp.mCoordinate, LUT_bandIndices=LUT_bandIndices)

                #2. visualize already loaded data
                LUTStyle = {}
                for style in self.plotSettingsModel3D:
                    assert isinstance(style, TemporalProfile3DPlotStyle)
                    LUTStyle[style.sensor()] = style
                dataPos = []
                x0 = x1 = y0 = y1 = z0 = z1 = 0
                for iDate, tsd in enumerate(tp.mTimeSeries):
                    data = tp.data(tsd)
                    bandKeys = sorted([k for k in data.keys() if k.startswith('b') and data[k] != None], key=lambda k: bandKey2bandIndex(k))
                    if len(bandKeys) < 2:
                        continue
                    for i, k in enumerate(bandKeys):
                        x.append(i)
                        y.append(t)
                        z.append(data[k])
                    x = np.asarray(x)
                    y = np.asarray(y)
                    z = np.asarray(z)
                    if iDate == 0:
                        x0, x1 = (x.min(), x.max())
                        y0, y1 = (y.min(), y.max())
                        z0, z1 = (z.min(), z.max())
                    else:
                        x0, x1 = (min(x.min(), x0), max(x.max(), x1))
                        y0, y1 = (min(y.min(), y0), max(y.max(), y1))
                        z0, z1 = (min(z.min(), z0), max(z.max(), z1))
                    if tsd.sensor in LUTStyle.keys():
                        style = LUTStyle[tsd.sensor]
                    else:
                        style = TemporalProfile3DPlotStyle(tsd.sensor)
                    dataPos.append((x,y,z, style))

                xyz = [(x0,x1),(y0,y1),(z0,z1)]
                l = len(dataPos)
                for iPos, pos in enumerate(dataPos):
                    x,y,z, style = pos
                    assert isinstance(style, TemporalProfile3DPlotStyle)
                    if not style.isVisible():
                        continue
                    arr = np.asarray((x,y,z), dtype=np.float64).transpose()
                    for i, m in enumerate(xyz):
                        arr[:, i] = (arr[:,i] - m0)/(m1-m0)

                    plt = gl.GLLinePlotItem(pos=arr,
                                            #color=pg.glColor((i, n * 1.3)),
                                            #color=pg.glColor(255,123,123,125),
                                            #color=pg.glColor((iPos, l * 1.3)),
                                            color=pg.glColor(style.color()),
                    self.glPlotDataItems.append(plt)

                for i, item in enumerate(self.glPlotDataItems):
                   w.addItem(item)
                #self.glGridItem.scale(0.1,0.1,0.1, local=False)

                #w.setBackgroundColor(QColor('black'))
                #w.setCameraPosition(pos=(0.0, 0.0, 0.0), distance=1.)
                w.addItem(self.glGridItem)
                w.update()
                """
                for sensor, values in data.items():
                    if len(values['z']) > 0:
                        x = values['x']
                        y = values['y']
                        z = values['z']
                        
                        p2 = gl.GLSurfacePlotItem(x=x, y=y, z=z, shader='normalColor')
                        p2.translate(-10, -10, 0)
                        w.addItem(p2)
                """

    @QtCore.pyqtSlot()
    def updatePlot2D(self):
        if isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
            if DEBUG:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                print('Update plot...')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            pi = self.plot2D.getPlotItem()
            piDataItems = pi.listDataItems()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            locations = set()
            for plotSetting in self.plotSettingsModel2D:
                assert isinstance(plotSetting, TemporalProfile2DPlotStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                locations.add(plotSetting.temporalProfile().mCoordinate)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                for pdi in plotSetting.mPlotItems:
                    assert isinstance(pdi, TemporalProfilePlotDataItem)
                    pdi.updateDataAndStyle()

            #for i in pi.dataItems:
            #    i.updateItems()

            #self.plot2D.update()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            #2. load pixel data
            self.loadCoordinate(list(locations))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            # https://github.com/pyqtgraph/pyqtgraph/blob/5195d9dd6308caee87e043e859e7e553b9887453/examples/customPlot.py
            return
def examplePixelLoader():

    # prepare QGIS environment
    if sys.platform == 'darwin':
        PATH_QGS = r'/Applications/QGIS.app/Contents/MacOS'
        os.environ['GDAL_DATA'] = r'/usr/local/Cellar/gdal/1.11.3_1/share'
    else:
        # assume OSGeo4W startup
        PATH_QGS = os.environ['QGIS_PREFIX_PATH']
    assert os.path.exists(PATH_QGS)

    qgsApp = QgsApplication([], True)
    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns')
    QApplication.addLibraryPath(r'/Applications/QGIS.app/Contents/PlugIns/qgis')
    qgsApp.setPrefixPath(PATH_QGS, True)
    qgsApp.initQgis()


    gb = QGroupBox()
    gb.setTitle('Sandbox')

    PL = PixelLoader()
    PL.setNumberOfThreads(2)

    if False:
        files = ['observationcloud/testdata/2014-07-26_LC82270652014207LGN00_BOA.bsq',
                 'observationcloud/testdata/2014-08-03_LE72270652014215CUB00_BOA.bsq'
                 ]
    else:
        from timeseriesviewer.utils import file_search
        searchDir = r'H:\LandsatData\Landsat_NovoProgresso'
        files = file_search(searchDir, '*227065*band4.img', recursive=True)
        #files = files[0:3]

    lyr = QgsRasterLayer(files[0])
    coord = lyr.extent().center()
    crs = lyr.crs()

    l = QVBoxLayout()

    btnStart = QPushButton()
    btnStop = QPushButton()
    prog = QProgressBar()
    tboxResults = QPlainTextEdit()
    tboxResults.setMaximumHeight(300)
    tboxThreads = QPlainTextEdit()
    tboxThreads.setMaximumHeight(200)
    label = QLabel()
    label.setText('Progress')

    def showProgress(n,m,md):
        prog.setMinimum(0)
        prog.setMaximum(m)
        prog.setValue(n)

        info = []
        for k, v in md.items():
            info.append('{} = {}'.format(k,str(v)))
        tboxResults.setPlainText('\n'.join(info))
        #tboxThreads.setPlainText(PL.threadInfo())
        qgsApp.processEvents()

    PL.sigPixelLoaded.connect(showProgress)
    btnStart.setText('Start loading')
    btnStart.clicked.connect(lambda : PL.startLoading(files, coord, crs))
    btnStop.setText('Cancel')
    btnStop.clicked.connect(lambda: PL.cancelLoading())
    lh = QHBoxLayout()
    lh.addWidget(btnStart)
    lh.addWidget(btnStop)
    l.addLayout(lh)
    l.addWidget(prog)
    l.addWidget(tboxThreads)
    l.addWidget(tboxResults)

    gb.setLayout(l)
    gb.show()
    #rs.setBackgroundStyle('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #222, stop:1 #333);')
    #rs.handle.setStyleSheet('background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #282, stop:1 #393);')
    qgsApp.exec_()
    qgsApp.exitQgis()

if __name__ == '__main__':
    import site, sys
    from timeseriesviewer import utils
    qgsApp = utils.initQgisApplication()
    DEBUG = False
    if False: #the ultimative test for floating point division correctness, at least on a DOY-level
        date1 = np.datetime64('1960-12-31','D')
        assert date1 == num2date(date2num(date1))
        #1960 - 12 - 31
        for year in  range(1960, 2057):
            for doy in range(1, daysPerYear(year)+1):
                dt = datetime.timedelta(days=doy - 1)
                date1 = np.datetime64('{}-01-01'.format(year)) + np.timedelta64(doy-1,'D')
                date2 = datetime.date(year=year, month=1, day=1) + datetime.timedelta(days=doy-1)

                assert date1 == num2date(date2num(date1), dt64=True), 'date1: {}'.format(date1)
                assert date2 == num2date(date2num(date2), dt64=False), 'date2: {}'.format(date1)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    ui = ProfileViewDockUI()
    ui.show()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    if True:
        TS = TimeSeries()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        STVis = SpectralTemporalVisualization(ui)
        STVis.setTimeSeries(TS)
        import example.Images
        from timeseriesviewer import file_search
        files = file_search(os.path.dirname(example.Images.__file__), '*.tif')
        TS.addFiles([files[0]])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        ext = TS.getMaxSpatialExtent()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        cp1 = SpatialPoint(ext.crs(),ext.center())
        cpND = SpatialPoint(ext.crs(), 681151.214,-752388.476)
        cp2 = SpatialPoint(ext.crs(), ext.center())
        cp3 = SpatialPoint(ext.crs(), ext.center().x()+500, ext.center().y()+250)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        STVis.loadCoordinate(cp2)
        STVis.loadCoordinate(cp3)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        STVis.createNewPlotStyle()
        if False:
            for tp in STVis.tpCollection:
                assert isinstance(tp, TemporalProfile)
                tp.plot()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    qgsApp.exec_()
    qgsApp.exitQgis()