Skip to content
Snippets Groups Projects
profilevisualization.py 80.6 KiB
Newer Older
  • Learn to ignore specific revisions
  •                 if w.isValidExpression() and expr != exprLast:
                        model.setData(index, w.asExpression(), Qt.EditRole)
    
                elif cname == model.cnStyle:
                    if isinstance(w, PlotStyleButton):
                        style = w.plotStyle()
                        model.setData(index, style, Qt.EditRole)
    
                elif cname == model.cnSensor:
                    assert isinstance(w, QComboBox)
                    sensor = w.itemData(w.currentIndex(), role=Qt.UserRole)
                    if isinstance(sensor, SensorInstrument):
                        model.setData(index, sensor, Qt.EditRole)
    
                elif cname == model.cnTemporalProfile:
                    assert isinstance(w, QgsFeatureListComboBox)
                    fid = w.identifierValue()
                    if isinstance(fid, int):
    
                        # once set manually, do not update to last temporal profile any more
                        oldStyle = index.data(role=Qt.UserRole)
                        if isinstance(oldStyle, TemporalProfile2DPlotStyle):
                            oldStyle.mShowLastLocation = False
    
                        TP = self.mTemporalProfileLayer.mProfiles.get(fid)
                        model.setData(index, TP, Qt.EditRole)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class PlotSettingsModel3DWidgetDelegate(QStyledItemDelegate):
        """
    
        """
        def __init__(self, tableView, temporalProfileLayer, parent=None):
            assert isinstance(tableView, QTableView)
            assert isinstance(temporalProfileLayer, TemporalProfileLayer)
            super(PlotSettingsModel3DWidgetDelegate, self).__init__(parent=parent)
            self._preferedSize = QgsFieldExpressionWidget().sizeHint()
            self.mTableView = tableView
            self.mTimeSeries = temporalProfileLayer.timeSeries()
            self.mTemporalProfileLayer = temporalProfileLayer
            self.mSensorLayers = {}
    
        def setItemDelegates(self, tableView):
            assert isinstance(tableView, QTableView)
            model = tableView.model()
            assert isinstance(model, PlotSettingsModel3D)
            for c in [model.cnSensor,
                      model.cnExpression,
                      # model.cnStyle,
                      model.cnTemporalProfile]:
                i = model.columnNames.index(c)
                tableView.setItemDelegateForColumn(i, self)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def getColumnName(self, index):
            assert index.isValid()
            model = index.model()
            assert isinstance(model, PlotSettingsModel3D)
            return model.columnNames[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.cnExpression:
                        w = QgsFieldExpressionWidget(parent=parent)
                        w.setExpression(plotStyle.expression())
                        w.setLayer(self.exampleLyr(plotStyle.sensor()))
                        def onSensorAdded(s):
                            w.setLayer(self.exampleLyr(s))
                        #plotStyle.sigSensorChanged.connect(lambda s : w.setLayer(self.exampleLyr(s)))
                        plotStyle.sigSensorChanged.connect(onSensorAdded)
                        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 = TemporalProfile3DPlotStyleButton(parent=parent)
                        w.setPlotStyle(plotStyle)
                        w.setToolTip('Set plot 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)
                        w.setModel(m)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    elif cname == model.cnTemporalProfile:
                        w = QgsFeatureListComboBox(parent=parent)
                        w.setSourceLayer(self.mTemporalProfileLayer)
                        w.setIdentifierField('id')
                        w.setDisplayExpression('to_string("id")+\'  \'+"name"')
                        w.setAllowNull(False)
                    else:
                        raise NotImplementedError()
            return w
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if sensor not in self.mSensorLayers.keys():
                crs = QgsCoordinateReferenceSystem('EPSG:4862')
                uri = 'Point?crs={}'.format(crs.authid())
                lyr = QgsVectorLayer(uri, 'LOCATIONS', 'memory')
                assert sensor is None or isinstance(sensor, SensorInstrument)
    
                f = sensorExampleQgsFeature(sensor, singleBandOnly=True)
                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 checkData(self, index, w, value):
            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, QgsFieldExpressionWidget):
                    assert value == w.expression()
                    assert w.isExpressionValid(value) == w.isValidExpression()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    if w.isValidExpression():
                        self.commitData.emit(w)
                    else:
                        s = ""
                        #print(('Delegate commit failed',w.asExpression()))
                if isinstance(w, TemporalProfile3DPlotStyleButton):
    
        def setEditorData(self, editor, index):
            cname = self.getColumnName(index)
            model = self.mTableView.model()
    
            w = None
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
                cname = self.getColumnName(index)
                style = model.idx2plotStyle(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:
                    assert isinstance(editor, TemporalProfile3DPlotStyleButton)
                    editor.setPlotStyle(style)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                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, QgsFeatureListComboBox)
                    value = editor.identifierValue()
                    if value != QVariant():
                        plotStyle = index.data(role=Qt.UserRole)
                        TP = plotStyle.temporalProfile()
                        editor.setIdentifierValue(TP.id())
                    else:
                        s  = ""
    
        def setModelData(self, w, model, index):
            cname = self.getColumnName(index)
            model = self.mTableView.model()
    
            if index.isValid() and isinstance(model, PlotSettingsModel3D):
                if cname == model.cnExpression:
                    assert isinstance(w, QgsFieldExpressionWidget)
                    expr = w.asExpression()
                    exprLast = model.data(index, Qt.DisplayRole)
    
                    if w.isValidExpression() and expr != exprLast:
                        model.setData(index, w.asExpression(), Qt.EditRole)
    
                elif cname == model.cnStyle:
                    assert isinstance(w, TemporalProfile3DPlotStyleButton)
                    model.setData(index, w.plotStyle(), Qt.EditRole)
    
                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, QgsFeatureListComboBox)
                    fid = w.identifierValue()
                    if isinstance(fid, int):
                        TP = self.mTemporalProfileLayer.mProfiles.get(fid)
                        model.setData(index, TP, Qt.EditRole)
    
    class ProfileViewDockUI(QgsDockWidget, loadUI('profileviewdock.ui')):
    
        def __init__(self, parent=None):
            super(ProfileViewDockUI, self).__init__(parent)
            self.setupUi(self)
    
            self.mActions2D = [self.actionAddStyle2D, self.actionRemoveStyle2D, self.actionRefresh2D, self.actionReset2DPlot]
            self.mActions3D = [self.actionAddStyle3D, self.actionRemoveStyle3D, self.actionRefresh3D,
                               self.actionReset3DCamera]
            self.mActionsTP = [self.actionLoadTPFromOgr, self.actionSaveTemporalProfiles, self.actionToggleEditing,
                               self.actionRemoveTemporalProfile, self.actionLoadMissingValues]
            #TBD.
            #self.line.setVisible(False)
            #self.listWidget.setVisible(False)
            self.baseTitle = self.windowTitle()
            self.stackedWidget.currentChanged.connect(self.onStackPageChanged)
    
            self.plotWidget3D = None
            self.plotWidget3DMPL = None
    
            #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
    
            self.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
    
        def init3DWidgets(self, mode='gl'):
            assert mode in ['gl']
            l = self.frame3DPlot.layout()
    
            if ENABLE_OPENGL and OPENGL_AVAILABLE and mode == 'gl':
    
                from eotimeseriesviewer.temporalprofiles3dGL import ViewWidget3D
                self.plotWidget3D = ViewWidget3D(parent=self.labelDummy3D.parent())
                self.plotWidget3D.setObjectName('plotWidget3D')
    
                size = self.labelDummy3D.size()
                l.addWidget(self.plotWidget3D)
                self.plotWidget3D.setSizePolicy(self.labelDummy3D.sizePolicy())
                self.labelDummy3D.setVisible(False)
                l.removeWidget(self.labelDummy3D)
                #self.plotWidget3D.setBaseSize(size)
                self.splitter3D.setSizes([100, 100])
                self.frameSettings3D.setEnabled(True)
            else:
                self.frameSettings3D.setEnabled(False)
    
        def onStackPageChanged(self, i):
            w = self.stackedWidget.currentWidget()
            title = self.baseTitle
            if w == self.page2D:
                title = '{} | 2D'.format(title)
                for a in self.mActions2D:
                    a.setVisible(True)
                for a in self.mActions3D + self.mActionsTP:
                    a.setVisible(False)
            elif w == self.page3D:
                title = '{} | 3D (experimental!)'.format(title)
                for a in self.mActions2D + self.mActionsTP:
                    a.setVisible(False)
                for a in self.mActions3D:
                    a.setVisible(True)
            elif w == self.pagePixel:
                title = '{} | Coordinates'.format(title)
                for a in self.mActions2D + self.mActions3D:
                    a.setVisible(False)
                for a in self.mActionsTP:
                    a.setVisible(True)
    
        sigShowPixel = pyqtSignal(TimeSeriesDate, QgsPoint, QgsCoordinateReferenceSystem)
    
        """
        Signalizes to move to specific date of interest
        """
        sigMoveToDate = pyqtSignal(np.datetime64)
    
        def __init__(self, timeSeries, profileDock):
            super(SpectralTemporalVisualization, self).__init__()
    
            assert isinstance(profileDock, ProfileViewDockUI)
            self.ui = profileDock
    
            self.mTasks = dict()
            #import eotimeseriesviewer.pixelloader
            #if DEBUG:
            #    eotimeseriesviewer.pixelloader.DEBUG = True
    
            #the timeseries. will be set later
            assert isinstance(timeSeries, TimeSeries)
            self.TS = timeSeries
            self.plot_initialized = False
    
            self.plot2D = self.ui.plotWidget2D
            self.plot2D.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
            self.plot3D = self.ui.plotWidget3D
    
            # temporal profile collection to store loaded values
            self.mTemporalProfileLayer = TemporalProfileLayer(self.TS)
            self.mTemporalProfileLayer.sigTemporalProfilesAdded.connect(self.onTemporalProfilesAdded)
            #self.mTemporalProfileLayer.startEditing()
            self.mTemporalProfileLayer.selectionChanged.connect(self.onTemporalProfileSelectionChanged)
    
            # fix to not loose C++ reference on temporal profile layer in case it is removed from QGIS mapcanvas
            self.mMapCanvas = QgsMapCanvas()
            self.mMapCanvas.setVisible(False)
            self.mMapCanvas.setLayers([self.mTemporalProfileLayer])
            # self.tpCollectionListModel = TemporalProfileCollectionListModel(self.tpCollection)
    
            assert isinstance(self.ui.mDualView, QgsDualView)
            self.ui.mDualView.init(self.mTemporalProfileLayer, self.mMapCanvas)
            self.ui.mDualView.setView(QgsDualView.AttributeTable)
            # pixel loader to load pixel values in parallel
            config = QgsAttributeTableConfig()
            config.update(self.mTemporalProfileLayer.fields())
            config.setActionWidgetVisible(False)
    
            hidden = []
            for i, columnConfig in enumerate(config.columns()):
    
                assert isinstance(columnConfig, QgsAttributeTableConfig.ColumnConfig)
                config.setColumnHidden(i, columnConfig.name in hidden)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
            self.mTemporalProfilesTableConfig = config
            self.mTemporalProfileLayer.setAttributeTableConfig(self.mTemporalProfilesTableConfig)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            #self.pixelLoader = PixelLoader()
            #self.pixelLoader.sigPixelLoaded.connect(self.onPixelLoaded)
            #self.pixelLoader.sigLoadingStarted.connect(self.onLoadingStarted)
            #self.pixelLoader.sigLoadingFinished.connect(self.onLoadingFinished)
            #self.pixelLoader.sigProgressChanged.connect(self.onLoadingProgressChanged)
    
            # set the plot models for 2D
            self.plotSettingsModel2D = PlotSettingsModel2D()
            self.plotSettingsModel2DProxy = QSortFilterProxyModel()
            self.plotSettingsModel2DProxy.setSourceModel(self.plotSettingsModel2D)
            self.ui.tableView2DProfiles.setModel(self.plotSettingsModel2DProxy)
            self.ui.tableView2DProfiles.setSortingEnabled(True)
            self.ui.tableView2DProfiles.selectionModel().selectionChanged.connect(self.onPlot2DSelectionChanged)
            self.ui.tableView2DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
            self.plotSettingsModel2D.sigDataChanged.connect(self.requestUpdate)
            self.plotSettingsModel2D.rowsInserted.connect(self.onRowsInserted2D)
            self.delegateTableView2D = PlotSettingsModel2DWidgetDelegate(self.ui.tableView2DProfiles, self.mTemporalProfileLayer)
            self.delegateTableView2D.setItemDelegates(self.ui.tableView2DProfiles)
            #self.ui.tableView2DProfiles.horizontalHeader().sectionResized.connect(self.on2DSettingsTableColumnResize)
    
            # set the plot models for 3D
            self.plotSettingsModel3D = PlotSettingsModel3D()
            self.ui.tableView3DProfiles.setModel(self.plotSettingsModel3D)
            self.ui.tableView3DProfiles.setSortingEnabled(False)
            self.ui.tableView2DProfiles.selectionModel().selectionChanged.connect(self.onPlot3DSelectionChanged)
            self.ui.tableView3DProfiles.horizontalHeader().setResizeMode(QHeaderView.Interactive)
            self.plotSettingsModel3D.rowsInserted.connect(self.onRowsInserted3D)
            self.delegateTableView3D = PlotSettingsModel3DWidgetDelegate(self.ui.tableView3DProfiles, self.mTemporalProfileLayer)
            self.delegateTableView3D.setItemDelegates(self.ui.tableView3DProfiles)
    
            if not ENABLE_OPENGL:
                self.ui.listWidget.item(1).setHidden(True)
                self.ui.page3D.setHidden(True)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            def onTemporalProfilesRemoved(removedProfiles):
                #set to valid temporal profile
    
                affectedStyles2D = [p for p in self.plotSettingsModel2D if p.temporalProfile() in removedProfiles]
                affectedStyles3D = [p for p in self.plotSettingsModel3D if p.temporalProfile() in removedProfiles]
                alternativeProfile = self.mTemporalProfileLayer[-1] if len(self.mTemporalProfileLayer) > 0 else None
                for s in affectedStyles2D:
                    assert isinstance(s, TemporalProfile2DPlotStyle)
                    m = self.plotSettingsModel2D
                    idx = m.plotStyle2idx(s)
                    idx = m.createIndex(idx.row(), m.columnNames.index(m.cnTemporalProfile))
                    m.setData(idx, alternativeProfile, Qt.EditRole)
    
                for s in affectedStyles3D:
                    assert isinstance(s, TemporalProfile3DPlotStyle)
                    m = self.plotSettingsModel3D
                    idx = m.plotStyle2idx(s)
                    idx = m.createIndex(idx.row(), m.columnNames.index(m.cnTemporalProfile))
                    m.setData(idx, alternativeProfile, Qt.EditRole)
    
            self.mTemporalProfileLayer.sigTemporalProfilesRemoved.connect(onTemporalProfilesRemoved)
    
            # remove / add plot style
            def on2DPlotStyleRemoved(plotStyles):
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, PlotStyle)
                    for pi in plotStyle.mPlotItems:
                        self.plot2D.plotItem.removeItem(pi)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            def on3DPlotStyleRemoved(plotStyles):
                toRemove = []
                for plotStyle in plotStyles:
                    assert isinstance(plotStyle, TemporalProfile3DPlotStyle)
                    toRemove.append(plotStyle.mPlotItems)
                self.plot3D.removeItems(toRemove)
    
            self.plotSettingsModel2D.sigPlotStylesRemoved.connect(on2DPlotStyleRemoved)
            self.plotSettingsModel3D.sigPlotStylesRemoved.connect(on3DPlotStyleRemoved)
    
            # initialize the update loop
    
            self.updateRequested = True
    
            self.updateTimer = QTimer(self)
            self.updateTimer.timeout.connect(self.onDataUpdate)
            self.updateTimer.start(2000)
    
            self.initActions()
            #self.ui.stackedWidget.setCurrentPage(self.ui.pagePixel)
            self.ui.onStackPageChanged(self.ui.stackedWidget.currentIndex())
    
        def onLoadingStarted(self):
            self.ui.progressInfo.setText('Start loading...')
            self.ui.progressBar.setRange(0, 0)
            self.ui.progressBar.setValue(0)
    
        def onLoadingProgressChanged(self, progress):
            if self.ui.progressBar.maximum() == 0:
                self.ui.progressBar.setRange(0, 100)
    
    
            value = int(progress)
            self.ui.progressBar.setValue(value)
    
            if value == 100:
                self.plot2D.enableAutoRange('x', False)
                self.ui.progressInfo.setText('')
                self.ui.progressBar.setValue(0)
    
        def plotStyles(self):
            return self.plotSettingsModel2D[:]
    
        def temporalProfileLayer(self)->TemporalProfileLayer:
    
            Returns a QgsVectorLayer that is used to store profile coordinates.
            :return:
    
        def onTemporalProfilesContextMenu(self, event):
            assert isinstance(event, QContextMenuEvent)
            tableView = self.ui.tableViewTemporalProfiles
            selectionModel = self.ui.tableViewTemporalProfiles.selectionModel()
            assert isinstance(selectionModel, QItemSelectionModel)
    
            model = self.ui.tableViewTemporalProfiles.model()
            assert isinstance(model, TemporalProfileLayer)
    
            if len(selectionModel.selectedIndexes()) > 0:
                for idx in selectionModel.selectedIndexes():
                    tp = model.idx2tp(idx)
                    if isinstance(tp, TemporalProfile) and not tp in temporalProfiles:
                        temporalProfiles.append(tp)
            else:
                temporalProfiles = model[:]
    
            spatialPoints = [tp.coordinate() for tp in temporalProfiles]
    
            a = menu.addAction('Load missing')
            a.setToolTip('Loads missing band-pixels.')
            a.triggered.connect(lambda : self.loadCoordinate(spatialPoints=spatialPoints, mode='all'))
            s = ""
    
            a = menu.addAction('Reload')
            a.setToolTip('Reloads all band-pixels.')
            a.triggered.connect(lambda: self.loadCoordinate(spatialPoints=spatialPoints, mode='reload'))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            menu.popup(tableView.viewport().mapToGlobal(event.pos()))
            self.menu = menu
    
            m = self.ui.tableView2DProfiles.model()
            for idx in selectedModelIndices(self.ui.tableView2DProfiles):
                result.append(m.data(idx, Qt.UserRole))
            return result
    
        def removePlotStyles2D(self, plotStyles):
            m = self.ui.tableView2DProfiles.model()
            if isinstance(m.sourceModel(), PlotSettingsModel2D):
                m.sourceModel().removePlotStyles(plotStyles)
    
            self.mTemporalProfileLayer.selectByIds(fids)
            b = self.mTemporalProfileLayer.isEditable()
            self.mTemporalProfileLayer.startEditing()
            self.mTemporalProfileLayer.deleteSelectedFeatures()
            self.mTemporalProfileLayer.saveEdits(leaveEditable=b)
    
        def createNewPlotStyle2D(self):
            l = len(self.mTemporalProfileLayer)
    
            plotStyle = TemporalProfile2DPlotStyle()
            plotStyle.sigExpressionUpdated.connect(self.updatePlot2D)
    
            sensors = self.TS.sensors()
            if len(sensors) > 0:
                plotStyle.setSensor(sensors[0])
    
            if len(self.mTemporalProfileLayer) > 0:
                temporalProfile = self.mTemporalProfileLayer[0]
                plotStyle.setTemporalProfile(temporalProfile)
    
            if len(self.plotSettingsModel2D) > 0:
                lastStyle = self.plotSettingsModel2D[0] #top style in list is the most-recent
                assert isinstance(lastStyle, TemporalProfile2DPlotStyle)
                markerColor = nextColor(lastStyle.markerBrush.color())
                plotStyle.markerBrush.setColor(markerColor)
            self.plotSettingsModel2D.insertPlotStyles([plotStyle], i=0)
            pdi = plotStyle.createPlotItem(self.plot2D)
    
            assert isinstance(pdi, TemporalProfilePlotDataItem)
            pdi.sigClicked.connect(self.onProfileClicked2D)
            pdi.sigPointsClicked.connect(self.onPointsClicked2D)
            self.plot2D.plotItem.addItem(pdi)
            #self.plot2D.getPlotItem().addItem(pg.PlotDataItem(x=[1, 2, 3], y=[1, 2, 3]))
            #plotItem.addDataItem(pdi)
            #plotItem.plot().sigPlotChanged.emit(plotItem)
            self.updatePlot2D()
            return plotStyle
    
        def createNewPlotStyle3D(self):
            if not (ENABLE_OPENGL and OPENGL_AVAILABLE):
                return
    
            plotStyle = TemporalProfile3DPlotStyle()
            plotStyle.sigExpressionUpdated.connect(self.updatePlot3D)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if len(self.mTemporalProfileLayer) > 0:
                temporalProfile = self.mTemporalProfileLayer[0]
                plotStyle.setTemporalProfile(temporalProfile)
                if len(self.plotSettingsModel3D) > 0:
                    color = self.plotSettingsModel3D[-1].color()
                    plotStyle.setColor(nextColor(color))
    
            sensors = list(self.TS.mSensors2TSDs.keys())
            if len(sensors) > 0:
                plotStyle.setSensor(sensors[0])
    
            self.plotSettingsModel3D.insertPlotStyles([plotStyle], i=0) # latest to the top
            plotItems = plotStyle.createPlotItem()
            self.plot3D.addItems(plotItems)
            #self.updatePlot3D()
    
    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):
                    c = tp.coordinate()
                    info = ['Sensor:{}'.format(sensor.name()),
                            'Coordinate:{}, {}'.format(c.x(), c.y())]
                    self.ui.tbInfo2D.setPlainText('\n'.join(info))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onPointsClicked2D(self, pdi, spottedItems):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            info = []
    
            if isinstance(pdi, TemporalProfilePlotDataItem) and isinstance(spottedItems, list):
                sensor = pdi.mPlotStyle.sensor()
                tp = pdi.mPlotStyle.temporalProfile()
                if isinstance(tp, TemporalProfile) and isinstance(sensor, SensorInstrument):
                    c = tp.coordinate()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    info.append('Sensor: {}'.format(sensor.name()))
                    info.append('Coordinate: {}, {}'.format(c.x(), c.y()))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    for item in spottedItems:
                        pos = item.pos()
                        x = pos.x()
                        y = pos.y()
                        date = num2date(x)
                        info.append('Date: {}\nValue: {}'.format(date, y))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            else:
                self.ui.tbInfo2D.setPlainText('\n'.join(info))
    
        def onTemporalProfilesAdded(self, profiles):
            # self.mTemporalProfileLayer.prune()
            for plotStyle in self.plotSettingsModel3D:
                assert isinstance(plotStyle, TemporalProfilePlotStyleBase)
                if not isinstance(plotStyle.temporalProfile(), TemporalProfile):
    
                    r = self.plotSettingsModel3D.plotStyle2idx(plotStyle).row()
                    c = self.plotSettingsModel3D.columnIndex(self.plotSettingsModel3D.cnTemporalProfile)
                    idx = self.plotSettingsModel3D.createIndex(r, c)
                    self.plotSettingsModel3D.setData(idx, self.mTemporalProfileLayer[0])
    
        def onTemporalProfileSelectionChanged(self, selectedFIDs, deselectedFIDs):
            nSelected = len(selectedFIDs)
    
            self.ui.actionRemoveTemporalProfile.setEnabled(nSelected > 0)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onPlot2DSelectionChanged(self, selected, deselected):
    
            self.ui.actionRemoveStyle2D.setEnabled(len(selected) > 0)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # todo: highlight selected profiles in plot
    
    
    
        def onPlot3DSelectionChanged(self, selected, deselected):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.ui.actionRemoveStyle3D.setEnabled(len(selected) > 0)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.ui.actionRemoveStyle2D.setEnabled(False)
            self.ui.actionRemoveTemporalProfile.setEnabled(False)
            self.ui.actionAddStyle2D.triggered.connect(self.createNewPlotStyle2D)
            self.ui.actionAddStyle3D.triggered.connect(self.createNewPlotStyle3D)
            self.ui.actionRefresh2D.triggered.connect(self.updatePlot2D)
            self.ui.actionRefresh3D.triggered.connect(self.updatePlot3D)
            self.ui.actionRemoveStyle2D.triggered.connect(lambda:self.removePlotStyles2D(self.selected2DPlotStyles()))
            self.ui.actionRemoveTemporalProfile.triggered.connect(lambda :self.removeTemporalProfiles(self.mTemporalProfileLayer.selectedFeatureIds()))
            self.ui.actionToggleEditing.triggered.connect(self.onToggleEditing)
            self.ui.actionReset2DPlot.triggered.connect(self.plot2D.resetViewBox)
            self.plot2D.resetTransform()
            self.ui.actionReset3DCamera.triggered.connect(self.reset3DCamera)
    
            self.ui.actionLoadTPFromOgr.triggered.connect(self.onLoadFromVector)
    
            self.ui.actionLoadMissingValues.triggered.connect(lambda: self.loadMissingData())
            self.ui.actionSaveTemporalProfiles.triggered.connect(lambda *args : self.mTemporalProfileLayer.saveTemporalProfiles)
            #set actions to be shown in the TemporalProfileTableView context menu
            ma = [self.ui.actionSaveTemporalProfiles, self.ui.actionLoadMissingValues]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onLoadFromVector(self):
    
            from .externals.qps.layerproperties import SelectMapLayersDialog
    
            d = SelectMapLayersDialog()
            d.addLayerDescription('Vector Layer', QgsMapLayerProxyModel.VectorLayer)
            d.setWindowTitle('Select Vector Layer')
            if d.exec() == QDialog.Accepted:
                for l in d.mapLayers():
                    self.mTemporalProfileLayer.loadCoordinatesFromOgr(l.source())
                    break
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if self.mTemporalProfileLayer.isEditable():
                self.mTemporalProfileLayer.saveEdits(leaveEditable=False)
            else:
                self.mTemporalProfileLayer.startEditing()
            self.onEditingToggled()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onEditingToggled(self):
            lyr = self.mTemporalProfileLayer
    
            hasSelectedFeatures = lyr.selectedFeatureCount() > 0
            isEditable = lyr.isEditable()
            self.ui.actionToggleEditing.blockSignals(True)
            self.ui.actionToggleEditing.setChecked(isEditable)
            #self.actionSaveTemporalProfiles.setEnabled(isEditable)
            #self.actionReload.setEnabled(not isEditable)
            self.ui.actionToggleEditing.blockSignals(False)
    
            #self.actionAddAttribute.setEnabled(isEditable)
            #self.actionRemoveAttribute.setEnabled(isEditable)
            self.ui.actionRemoveTemporalProfile.setEnabled(isEditable and hasSelectedFeatures)
            #self.actionPasteFeatures.setEnabled(isEditable)
            self.ui.actionToggleEditing.setEnabled(not lyr.readOnly())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if ENABLE_OPENGL and OPENGL_AVAILABLE:
                self.ui.actionReset3DCamera.trigger()
    
        sigMoveToTSD = pyqtSignal(TimeSeriesDate)
    
        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, qgsTask ,dump):
    
            tasks = pickle.loads(dump)
            assert isinstance(tasks, list)
            s = ""
            t0 = time.time()
            for task in tasks:
                assert isinstance(task, TemporalProfileLoaderTask)
    
                if len(task.mRESULTS) > 0:
                    for tpId, data in task.mRESULTS.items():
                        tp = self.mTemporalProfileLayer.mProfiles.get(tpId)
    
                        if isinstance(tp, TemporalProfile):
                            tsd = tp.timeSeries().getTSD(task.mSourcePath)
                            if isinstance(tsd, TimeSeriesDate):
                                tp.updateData(tsd, data)
    
                if len(task.mERRORS) > 0:
    
    
                    s = ""
            if True:
                print('WRESULTS {}'.format(time.time() - t0))
    
        def requestUpdate(self, *args):
            self.updateRequested = True
            #next time
    
        def onRowsInserted2D(self, parent, start, end):
            model = self.ui.tableView2DProfiles.model().sourceModel()
            if isinstance(model, PlotSettingsModel2D):
                colExpression = model.columnIndex(model.cnExpression)
                colStyle = model.columnIndex(model.cnStyle)
                while start <= end:
                    idxExpr = model.createIndex(start, colExpression)
                    idxStyle = model.createIndex(start, colStyle)
                    self.ui.tableView2DProfiles.openPersistentEditor(idxExpr)
                    self.ui.tableView2DProfiles.openPersistentEditor(idxStyle)
                    start += 1
    
        def onRowsInserted3D(self, parent, start, end):
            model = self.ui.tableView3DProfiles.model()
            if isinstance(model, PlotSettingsModel3D):
                colExpression = model.columnIndex(model.cnExpression)
                colStyle = model.columnIndex(model.cnStyle)
                while start <= end:
                    idxStyle = model.createIndex(start, colStyle)
                    idxExpr = model.createIndex(start, colExpression)
                    self.ui.tableView3DProfiles.openPersistentEditor(idxStyle)
                    self.ui.tableView3DProfiles.openPersistentEditor(idxExpr)
                    start += 1
    
        def onObservationClicked(self, plotDataItem, points):
            for p in points:
                tsd = p.data()
                #print(tsd)
    
        def loadMissingData(self, backgroundProcess=False):
            """
            Loads all band values of collected locations that have not been loaded until now
            """
    
            fids = self.mTemporalProfileLayer.selectedFeatureIds()
            if len(fids) == 0:
                fids = [f.id() for f in self.mTemporalProfileLayer.getFeatures()]
    
            tps = [self.mTemporalProfileLayer.mProfiles.get(fid) for fid in fids]
            spatialPoints = [tp.coordinate() for tp in tps if isinstance(tp, TemporalProfile)]
            self.loadCoordinate(spatialPoints=spatialPoints, mode='all', backgroundProcess=backgroundProcess)
    
        LOADING_MODES = ['missing', 'reload', 'all']
        def loadCoordinate(self, spatialPoints=None, LUT_bandIndices=None, mode='missing', backgroundProcess = True):
            """
            :param spatialPoints: [list-of-geometries] to load pixel values from
            :param LUT_bandIndices: dictionary {sensor:[indices]} with band indices to be loaded per sensor
            :param mode:
            :return:
            """
            """
            Loads a temporal profile for a single or multiple geometries.
            :param spatialPoints: SpatialPoint | [list-of-SpatialPoints]
            """
            assert mode in SpectralTemporalVisualization.LOADING_MODES
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if isinstance(spatialPoints, SpatialPoint):
                spatialPoints = [spatialPoints]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if not isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
                return False
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            # if not self.pixelLoader.isReadyToLoad():
            #    return False
    
            # Get or create the TimeSeriesProfiles which will store the loaded values
            tasks = []
    
            TemporalProfiles = []
    
            # Define which (new) bands need to be loaded for each sensor
            if LUT_bandIndices is None:
                LUT_bandIndices = dict()
                for sensor in self.TS.sensors():
                    if mode in ['all', 'reload']:
                        LUT_bandIndices[sensor] = list(range(sensor.nb))
                    else:
                        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()
    
            for spatialPoint in spatialPoints:
                assert isinstance(spatialPoint, SpatialPoint)
                TP = self.mTemporalProfileLayer.fromSpatialPoint(spatialPoint)
    
    
                # if no TemporalProfile existed before, create an empty one
    
                if not isinstance(TP, TemporalProfile):
                    TP = self.mTemporalProfileLayer.createTemporalProfiles(spatialPoint)[0]
    
    
                    # set existing plot style to current coordinate
                    for plotStyle in self.plotSettingsModel2D:
                        assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
                        if plotStyle.showLastLocation():
                            r = self.plotSettingsModel2D.plotStyle2idx(plotStyle).row()
                            c = self.plotSettingsModel2D.columnIndex(self.plotSettingsModel2D.cnTemporalProfile)
                            idx = self.plotSettingsModel2D.index(r, c)
                            self.plotSettingsModel2D.setData(idx, TP, role=Qt.EditRole)
                            plotStyle.setTemporalProfile(TP)
    
                    # create at least 1 plot style
    
                    if len(self.mTemporalProfileLayer) == 1:
                        if len(self.plotSettingsModel2D) == 0:
                            self.createNewPlotStyle2D()
    
                        if len(self.plotSettingsModel3D) == 0:
                            self.createNewPlotStyle3D()
    
    
                TemporalProfiles.append(TP)
    
            # each TSD is a Task
            s = ""
            # a Task defines which bands are to be loaded
            for tsd in self.TS:
    
                assert isinstance(tsd, TimeSeriesDate)
    
                # 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
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    for TP in TemporalProfiles:
    
                        assert isinstance(TP, TemporalProfile)
                        need2load = TP.missingBandIndices(tsd, requiredIndices=requiredIndices)
                        missingIndices = missingIndices.union(need2load)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    missingIndices = sorted(list(missingIndices))
                else:
                    missingIndices = requiredIndices
    
                    for tss in tsd.sources():
                        assert isinstance(tss, TimeSeriesSource)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
                        intersectingTPs = []
                        tssExtent = tss.spatialExtent()
    
                        for TP in TemporalProfiles:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                            assert isinstance(TP, TemporalProfile)
                            if tssExtent.contains(TP.coordinate().toCrs(tssExtent.crs())):
                                intersectingTPs.append(TP)
    
                        if len(intersectingTPs) > 0:
    
                            task = TemporalProfileLoaderTask(tss, intersectingTPs)
                            tasks.append(task)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
                        # pickle.loads(doLoaderTask(mock, pickle.dumps([t])))[0]
            if len(tasks) > 0:
                dump = pickle.dumps(tasks)
                if not backgroundProcess:
                    qgsTask = TaskMock()
                else:
                    qgsTask = QgsTask.fromFunction('Load Profiles', doLoadTemporalProfileTasks, dump,
                              on_finished = self.onPixelLoaded)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                tid = id(qgsTask)
                qgsTask.progressChanged.connect(self.onLoadingProgressChanged)
                qgsTask.taskCompleted.connect(lambda *args, tid=tid: self.onRemoveTask(tid))
                qgsTask.taskTerminated.connect(lambda *args, tid=tid: self.onRemoveTask(tid))
                self.mTasks[tid] = qgsTask
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                if not backgroundProcess:
                    self.onPixelLoaded(qgsTask, doLoaderTask(qgsTask, dump))
                else:
    
                    tm = QgsApplication.taskManager()
                    assert isinstance(tm, QgsTaskManager)
                    tm.addTask(qgsTask, 1000)
    
    
                if not backgroundProcess:
                    QApplication.processEvents()
    
            else:
                if DEBUG:
                    print('Data for geometries already loaded')
    
        def onRemoveTask(self, tid):
            if tid in self.mTasks.keys():
                del self.mTasks[tid]
    
            for plotSetting in self.plotSettingsModel2D:
                assert isinstance(plotSetting, TemporalProfile2DPlotStyle)
                tp = plotSetting.temporalProfile()
                for pdi in plotSetting.mPlotItems:
                    assert isinstance(pdi, TemporalProfilePlotDataItem)
                    pdi.updateDataAndStyle()
                if isinstance(tp, TemporalProfile) and plotSetting.temporalProfile().updated():
                    plotSetting.temporalProfile().resetUpdatedFlag()
    
            for i in self.plot2D.plotItem.dataItems:
                i.updateItems()
    
            notInit = [0, 1] == self.plot2D.plotItem.getAxis('bottom').range
            if notInit:
                x0 = x1 = None
                for plotSetting in self.plotSettingsModel2D:
                    assert isinstance(plotSetting, TemporalProfile2DPlotStyle)