Skip to content
Snippets Groups Projects
profilevisualization.py 103 KiB
Newer Older
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        """
        Sets the maximum number of temporal profiles to be stored in this container.
        :param n: number of profiles, must be >= 1
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert n >= 1
        if old != n:
            self.mMaxProfiles = n

            self.prune()
            self.sigMaxProfilesChanged.emit(self.mMaxProfiles)
Benjamin Jakimow's avatar
Benjamin Jakimow committed


    def prune(self):
        """
        Reduces the number of temporal profile to the value n defined with .setMaxProfiles(n)
        :return: [list-of-removed-TemporalProfiles]
        """
    def getFieldDefn(self, name, values):
        if isinstance(values, np.ndarray):
            # add bands
            if values.dtype in [np.int8, np.int16, np.int32, np.int64,
                                np.uint8, np.uint16, np.uint32, np.uint64]:
                fType = QVariant.Int
                fTypeName = 'integer'
            elif values.dtype in [np.float16, np.float32, np.float64]:
                fType = QVariant.Double
                fTypeName = 'decimal'
        else:
            raise NotImplementedError()

        return QgsField(name, fType, fTypeName)

    def setFeatureAttribute(self, feature, name, value):
        assert isinstance(feature, QgsFeature)
        assert isinstance(name, str)
        i = feature.fieldNameIndex(name)
        assert i >= 0, 'Field "{}" does not exist'.format(name)
        field = feature.fields()[i]
        if field.isNumeric():
            if field.type() == QVariant.Int:
                value = int(value)
            elif field.type() == QVariant.Double:
                value = float(value)
            else:
                raise NotImplementedError()
        feature.setAttribute(i, value)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def sort(self, col, order):
        if self.rowCount() == 0:
            return
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.layoutAboutToBeChanged.emit()
        colName = self.mColumNames[col]
        r = order != Qt.AscendingOrder
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if colName == self.mcnName:
            self.items.sort(key = lambda TP:TP.name(), reverse=r)
        elif colName == self.mcnCoordinate:
            self.items.sort(key=lambda TP: str(TP.mCoordinate), reverse=r)
        elif colName == self.mcnID:
            self.items.sort(key=lambda TP: TP.mID, reverse=r)
        elif colName == self.mcnLoaded:
            self.items.sort(key=lambda TP: TP.loadingStatus(), reverse=r)
        self.layoutChanged.emit()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def addPixelLoaderResult(self, d):
        assert isinstance(d, PixelLoaderTask)
        if d.success():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            for i, TPid in enumerate(d.temporalProfileIDs):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                TP = self.temporalProfileFromID(TPid)
                tsd = self.TS.getTSD(d.sourcePath)
                assert isinstance(tsd, TimeSeriesDatum)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if isinstance(TP, TemporalProfile):
                    profileData = d.resProfiles[i]
                    if not isinstance(profileData, tuple):
                        s = ""
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    vMean, vStd = profileData
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    values = {}
                    validValues = not isinstance(vMean, str)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    #1. add the pixel values per returned band
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    for iBand, bandIndex in enumerate(d.bandIndices):
                        key = 'b{}'.format(bandIndex + 1)
                        values[key] = vMean[iBand] if validValues else None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                        key = 'std{}'.format(bandIndex + 1)
                        values[key] = vStd[iBand] if validValues else None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    #indicesY, indicesX = d.imagePixelIndices()
                    #values['px_x'] = indicesX
                    #values['px_y'] = indicesY
                    TP.updateData(tsd, values)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #todo: remove TS Profiles
        #self.mTemporalProfiles.clear()
        #self.sensorPxLayers.clear()
        pass
class TemporalProfile2DPlotStyle(PlotStyle):
    sigExpressionUpdated = pyqtSignal()
    sigSensorChanged = pyqtSignal(SensorInstrument)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __init__(self, temporalProfile):
        super(TemporalProfile2DPlotStyle, self).__init__()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(temporalProfile, TemporalProfile)
        self.mSensor = None
        self.mTP = temporalProfile
        self.mExpression = u'"b1"'
        self.mPlotItems = []
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if isinstance(temporalProfile, TemporalProfile):
            self.setTemporalProfile(temporalProfile)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def createPlotItem(self, plotWidget):
        pdi = TemporalProfilePlotDataItem(self)
        self.mPlotItems.append(pdi)
        return pdi
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def temporalProfile(self):
        return self.mTP
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def setTemporalProfile(self, temporalPofile):
        assert isinstance(temporalPofile, TemporalProfile)
        b = temporalPofile != self.mTP
        self.mTP = temporalPofile
        if b: self.update()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def setSensor(self, sensor):
        assert isinstance(sensor, SensorInstrument)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        b = sensor != self.mSensor
        if b:
            self.update()
            self.sigSensorChanged.emit(sensor)
        super(TemporalProfile2DPlotStyle, self).update()

        for pdi in self.mPlotItems:
            assert isinstance(pdi, TemporalProfilePlotDataItem)
            pdi.updateStyle()
            #pdi.updateItems()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def sensor(self):
        return self.mSensor


    def setExpression(self, exp):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(exp, unicode)
        b = self.mExpression != exp
        if b:
            self.update()
            self.sigExpressionUpdated.emit()
    def __reduce_ex__(self, protocol):
        return self.__class__, (), self.__getstate__()

    def __getstate__(self):
        result = super(TemporalProfile2DPlotStyle, self).__getstate__()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        del result['mTP']
class TemporalProfile3DPlotStyle(PlotStyle):


    def __init__(self,sensor):
        super(TemporalProfile3DPlotStyle, self).__init__()
        #assert isinstance(temporalProfile, TemporalProfile)
        assert isinstance(sensor, SensorInstrument)
        self.mSensor = sensor
        #self.mTP = temporalProfile
        self.mScale = 1.0
        self.mOffset = 0.0
        self.mColor = QColor('green')

        #self.setTemporalProfile(temporalProfile)

    def setColor(self, color):
        assert isinstance(color, QColor)
        old = self.mColor
        self.mColor = color
        if old != color:
            self.update()

    def color(self):
        return self.mColor

    def setScaling(self, scale, offset):
        scale = float(scale)
        offset = float(offset)
        x,y =self.mScale, self.mOffset
        self.mScale = scale
        self.mOffset = offset

        if x != scale or y != offset:
            self.update()

    def sensor(self):
        return self.mSensor

    def __reduce_ex__(self, protocol):
        return self.__class__, (), self.__getstate__()

    def __getstate__(self):
        result = super(TemporalProfile3DPlotStyle, self).__getstate__()
        #remove
        del result['mSensor']

        return result


class DateTimeViewBox(pg.ViewBox):
    """
    Subclass of ViewBox
    """
    sigMoveToDate = pyqtSignal(np.datetime64)
    def __init__(self, parent=None):
        """
        Constructor of the CustomViewBox
        """
        super(DateTimeViewBox, self).__init__(parent)
        #self.menu = None # Override pyqtgraph ViewBoxMenu
        #self.menu = self.getMenu() # Create the menu
        #self.menu = None

    def raiseContextMenu(self, ev):

        pt = self.mapDeviceToView(ev.pos())
        print(pt.x(), pt.y())
        date = num2date(pt.x())
        menu = QMenu(None)
        a = menu.addAction('Move to {}'.format(date))
        a.setData(date)
        a.triggered.connect(lambda : self.sigMoveToDate.emit(date))
        self.scene().addParentContextMenus(self, menu, ev)
        menu.exec_(ev.screenPos().toPoint())



class DateTimePlotWidget(pg.PlotWidget):
    """
    Subclass of PlotWidget
    """
    def __init__(self, parent=None):
        """
        Constructor of the widget
        """
        super(DateTimePlotWidget, self).__init__(parent, viewBox=DateTimeViewBox())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.plotItem = pg.PlotItem(
            axisItems={'bottom':DateTimeAxis(orientation='bottom')},
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            viewBox=DateTimeViewBox()
        )
        self.setCentralItem(self.plotItem)
        self.xAxisInitialized = False
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.cnSensor = 'sensor'
        self.cnScale = 'Scale'
        self.cnOffset = 'Offset'
        self.cnStyle = 'style'

        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)
            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):
    #sigSensorAdded = pyqtSignal(SensorPlotSettings)
    sigVisibilityChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    sigDataChanged = pyqtSignal(TemporalProfile2DPlotStyle)
    sigPlotStylesAdded = pyqtSignal(list)
    sigPlotStylesRemoved = pyqtSignal(list)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    regBandKey = re.compile("(?<!\w)b\d+(?!\w)", re.IGNORECASE)
    regBandKeyExact = re.compile('^' + regBandKey.pattern + '$', re.IGNORECASE)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __init__(self, temporalProfileCollection, plotWidget, parent=None, *args):
        super(PlotSettingsModel2D, self).__init__(parent=parent)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(temporalProfileCollection, TemporalProfileCollection)

        self.cnID = 'ID'
        self.cnSensor = 'sensor'
        self.cnExpression = LABEL_DN
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.cnStyle = 'style'
        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 = PlotSettingsModel2D.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.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.baseTitle = self.windowTitle()
        self.TS = None
        self.progressBar.setMinimum(0)
        self.progressBar.setMaximum(100)
        self.progressBar.setValue(0)
        self.progressInfo.setText('')
        self.pxViewModel2D = None
        self.pxViewModel3D = None
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)

def qgsFieldFromKeyValue(fieldName, value):
    t = type(value)
    if t in [int, float] or np.isreal(value):

        fLen  = 0
        fPrec = 0
        fComm = ''
        fType = ''
        f = QgsField(fieldName, QVariant.Double, 'double', 40, 5)
    else:
        f = QgsField(fieldName, QVariant.String, 'text', 40, 5)
    return f

def sensorExampleQgsFeature(sensor):
    assert isinstance(sensor, SensorInstrument)
    #populate with exemplary band values (generally stored as floats)
    fieldValues = collections.OrderedDict()
    for b in range(sensor.nb):
        fn = bandIndex2bandKey(b)
        fieldValues[fn] = 1.0

    date = datetime.date.today()
    doy = dateDOY(date)
    fieldValues['doy'] = doy
    fieldValues['date'] = str(date)


    fields = QgsFields()
    for k, v in fieldValues.items():
        fields.append(qgsFieldFromKeyValue(k,v))
    f = QgsFeature(fields)
    for k, v in fieldValues.items():
        f.setAttribute(k, v)
    return f


def dateDOY(date):
    if isinstance(date, np.datetime64):
        date = date.astype(datetime.date)
    return date.timetuple().tm_yday

def daysPerYear(year):
    if isinstance(year, np.datetime64):
        year = year.astype(datetime.date)
    if isinstance(year, datetime.date):
        year = year.timetuple().tm_year

    return dateDOY(datetime.date(year=year, month=12, day=31))

    #kindly taken from https://stackoverflow.com/questions/6451655/python-how-to-convert-datetime-dates-to-decimal-years
    if isinstance(d, np.datetime64):
        d = d.astype(datetime.datetime)