Skip to content
Snippets Groups Projects
profilevisualization.py 75 KiB
Newer Older
  • Learn to ignore specific revisions
  • Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            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)
    
    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
    
            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
    
            pi = self.plot2D.getPlotItem()
            pi.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
            pi.getAxis('bottom').setLabel(LABEL_TIME)
            pi.getAxis('left').setLabel(LABEL_DN)
            self.plot2Dvline = pg.InfiniteLine(angle=90, movable=False)
            self.plot2Dhline = pg.InfiniteLine(angle=0, movable=False)
            #self.plot2DLabel = pg.TextItem(text='LABEL')
            self.plot2DLabel = pg.LabelItem(justify='right')
            #pi.setContentsMargins(0.1, 0.1, 0.1, 0.1)
            pi.addItem(self.plot2Dvline, ignoreBounds=True)
            pi.addItem(self.plot2Dhline, ignoreBounds=True)
            self.plot2D.addItem(self.plot2DLabel, ignoreBounds=True)
    
            self.proxy2D = pg.SignalProxy(self.plot2D.scene().sigMouseMoved, rateLimit=60, slot=self.onMouseMoved2D)
    
            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
    
            self.pixelLoader.sigLoadingStarted.connect(self.clear)
            self.pixelLoader.sigLoadingFinished.connect(lambda : self.plot2D.enableAutoRange('x', False))
    
    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)
    
    
            self.sigMoveToDate.connect(self.onMoveToDate)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.initActions()
    
    
    
        def onMouseMoved2D(self, evt):
            pos = evt[0]  ## using signal proxy turns original arguments into a tuple
    
            plotItem = self.plot2D.getPlotItem()
            if plotItem.sceneBoundingRect().contains(pos):
                mousePoint = plotItem.vb.mapSceneToView(pos)
                x = mousePoint.x()
                y = mousePoint.y()
    
                index = int(mousePoint.x())
                self.plot2DLabel.setText('Refreshed {}'.format(mousePoint.x(), mousePoint.y()))
                #if index > 0 and index < len(data1):
                #    label.setText(
                #        "<span style='font-size: 12pt'>x=%0.1f,   <span style='color: red'>y1=%0.1f</span>,   <span style='color: green'>y2=%0.1f</span>" % (
                #        mousePoint.x(), data1[index], data2[index]))
                self.plot2Dvline.setPos(mousePoint.x())
                self.plot2Dhline.setPos(mousePoint.y())
    
    
            return
            vb = plotItem.vb
    
    
            if plotItem.sceneBoundingRect().contains(pos):
                mousePoint = vb.mapSceneToView(pos)
    
                self.vLine.setPos(mousePoint.x())
                self.hLine.setPos(mousePoint.y())
    
                info = '{:0.2f}, {:0.2f}'.format(mousePoint.x(), mousePoint.y())
                self.tbCursorLocationValue.setText(info)
                """
                self.mlabel.setText(
                        "<span style='font-size: 12pt'>{}</span>".format(info)
                """
            else:
                self.tbCursorLocationValue.setText('')
                self.mlabel.setText('')
    
    
    
    
        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):
            self.ui.actionRemoveTemporalProfile.setEnabled(len(selected) > 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)
    
    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)
    
    
    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):
    
    
            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)
    
            # 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)
    
    
        sigMoveToTSD = pyqtSignal(TimeSeriesDatum)
    
        def onMoveToDate(self, date):
            dt = np.asarray([np.abs(tsd.date - date) for tsd in self.TS])
            i = np.argmin(dt)
            self.sigMoveToTSD.emit(self.TS[i])
    
    
        def onPixelLoaded(self, nDone, nMax, d):
            self.ui.progressBar.setValue(nDone)
            self.ui.progressBar.setMaximum(nMax)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(d, PixelLoaderTask)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            bn = os.path.basename(d.sourcePath)
    
            if d.success():
                t = 'Last loaded from {}.'.format(bn)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                self.tpCollection.addPixelLoaderResult(d)
    
                self.updateRequested = True
    
            else:
                t = 'Failed loading from {}.'.format(bn)
    
                if d.info and d.info != '':
                    t += '({})'.format(d.info)
    
            # QgsApplication.processEvents()
    
            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()
    
    
            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
    
    
            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)
    
                    existingBandKeys = [k for k in TP.data(tsd).keys() if regBandKeyExact.search(k)]
    
                    existingBandIndices = set([bandKey2bandIndex(k) for k in existingBandKeys])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    need2load = requiredIndices.difference(existingBandIndices)
                    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()
                    plotSetting.temporalProfile().resetUpdated()
    
            for i in self.plot2D.getPlotItem().dataItems:
                i.updateItems()
    
    
            if not self.plot2D.xAxisInitialized:
                x0 = x1 = None
    
                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):
    
    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'