Skip to content
Snippets Groups Projects
profilevisualization.py 74.8 KiB
Newer Older
        tableView = self.ui.tableViewTemporalProfiles
        selectionModel = self.ui.tableViewTemporalProfiles.selectionModel()
        assert isinstance(selectionModel, QItemSelectionModel)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        model = self.ui.tableViewTemporalProfiles.model()
        assert isinstance(model, TemporalProfileLayer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        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]
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        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'))
        menu.popup(tableView.viewport().mapToGlobal(event.pos()))
        self.menu = menu
    def selected2DPlotStyles(self):
        result = []
        m = self.ui.tableView2DProfiles.model()
        for idx in selectedModelIndices(self.ui.tableView2DProfiles):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def removePlotStyles2D(self, plotStyles):
        m = self.ui.tableView2DProfiles.model()
        if isinstance(m.sourceModel(), PlotSettingsModel2D):
            m.sourceModel().removePlotStyles(plotStyles)
    def removeTemporalProfiles(self, fids):
        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)
        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()
    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))
    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):
                c = tp.coordinate()
                info = ['Sensor: {}'.format(sensor.name()),
                        'Coordinate: {}, {}'.format(c.x(), c.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))
    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)
    def onPlot2DSelectionChanged(self, selected, deselected):
        self.ui.actionRemoveStyle2D.setEnabled(len(selected) > 0)
    def onPlot3DSelectionChanged(self, selected, deselected):
        self.ui.actionRemoveStyle3D.setEnabled(len(selected) > 0)
    def initActions(self):
        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(lambda : self.mTemporalProfileLayer.loadCoordinatesFromOgr(None))
        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]
        self.onEditingToggled()
    def onSaveTemporalProfiles(self):
    def onToggleEditing(self, b):
        if self.mTemporalProfileLayer.isEditable():
            self.mTemporalProfileLayer.saveEdits(leaveEditable=False)
        else:
            self.mTemporalProfileLayer.startEditing()
        self.onEditingToggled()
    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())
    def reset3DCamera(self, *args):
        if ENABLE_OPENGL and OPENGL_AVAILABLE:
            self.ui.actionReset3DCamera.trigger()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    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, d):
        if isinstance(d, PixelLoaderTask):
            bn = os.path.basename(d.sourcePath)
            if d.success():
Benjamin Jakimow's avatar
Benjamin Jakimow committed

                t = 'Loaded {} pixel from {}.'.format(len(d.resProfiles), bn)
                self.mTemporalProfileLayer.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.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
        if not isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
            return False
        # if not self.pixelLoader.isReadyToLoad():
        #    return False
        assert isinstance(self.TS, TimeSeries)
        # Get or create the TimeSeriesProfiles which will store the loaded values
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        tasks = []
        TPs = []
        theGeometries = []
        # 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)
        assert isinstance(LUT_bandIndices, dict)
        for sensor in self.TS.sensors():
            assert sensor in LUT_bandIndices.keys()
        #update new / existing points
        if isinstance(spatialPoints, SpatialPoint):
            spatialPoints = [spatialPoints]
        for spatialPoint in spatialPoints:
            assert isinstance(spatialPoint, SpatialPoint)
            TP = self.mTemporalProfileLayer.fromSpatialPoint(spatialPoint)
            # if not TP exists for this point, create an empty one
            if not isinstance(TP, TemporalProfile):
                TP = self.mTemporalProfileLayer.createTemporalProfiles(spatialPoint)[0]
                if len(self.mTemporalProfileLayer) == 1:
                    if len(self.plotSettingsModel2D) == 0:
                        self.createNewPlotStyle2D()
                    if len(self.plotSettingsModel3D) == 0:
                        self.createNewPlotStyle3D()
            TPs.append(TP)
            theGeometries.append(TP.coordinate())
        TP_ids = [TP.id() for TP in TPs]
        # each TSD is a Task
        s = ""
        # a Task defines which bands are to be loaded
        for tsd in self.TS:
            assert isinstance(tsd, TimeSeriesDatum)
            # 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
            if mode == 'missing':
                missingIndices = set()
                for TP in TPs:
                    assert isinstance(TP, TemporalProfile)
                    need2load = TP.missingBandIndices(tsd, requiredIndices=requiredIndices)
                    missingIndices = missingIndices.union(need2load)
                missingIndices = sorted(list(missingIndices))
            else:
                missingIndices = requiredIndices
            if len(missingIndices) > 0:
                for pathImg in tsd.sourceUris():
                    task = PixelLoaderTask(pathImg, theGeometries,
                                       bandIndices=missingIndices,
                                       temporalProfileIDs=TP_ids)
                tasks.append(task)
        if len(tasks) > 0:
            aGoodDefault = 2 if len(self.TS) > 25 else 1
            if DEBUG:
                print('Start loading for {} geometries from {} sources...'.format(
                    len(theGeometries), len(tasks)
                ))
            if backgroundProcess:
                self.pixelLoader.startLoading(tasks)
            else:
                import eotimeseriesviewer.pixelloader
                tasks = [PixelLoaderTask.fromDump(eotimeseriesviewer.pixelloader.doLoaderTask(None, task.toDump())) for task in tasks]
                l = len(tasks)
                for i, task in enumerate(tasks):
                    self.pixelLoader.sigPixelLoaded.emit(task)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        else:
            if DEBUG:
                print('Data for geometries already loaded')
    @QtCore.pyqtSlot()
    def onDataUpdate(self):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        # self.mTemporalProfileLayer.prune()

        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)
                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.plotItem.setXRange(x0, x1)
                # self.plot2D.xAxisInitialized = True
    @QtCore.pyqtSlot()
    def updatePlot3D(self):
        if ENABLE_OPENGL and OPENGL_AVAILABLE:
            from eotimeseriesviewer.temporalprofiles3dGL import ViewWidget3D
            assert isinstance(self.plot3D, ViewWidget3D)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            # 1. ensure that data from all bands will be loaded
            #    new loaded values will be shown in the next updatePlot3D call
            coordinates = []
            allPlotItems = []
            for plotStyle3D in self.plotSettingsModel3D:
                assert isinstance(plotStyle3D, TemporalProfile3DPlotStyle)
                if plotStyle3D.isPlotable():
                    coordinates.append(plotStyle3D.temporalProfile().coordinate())
            if len(coordinates) > 0:
                self.loadCoordinate(coordinates, mode='all')
            toRemove = [item for item in self.plot3D.items if item not in allPlotItems]
            self.plot3D.removeItems(toRemove)
            toAdd = [item for item in allPlotItems if item not in self.plot3D.items]
            self.plot3D.addItems(toAdd)
            """
            # 3. add new plot items
            plotItems = []
            for plotStyle3D in self.plotSettingsModel3D:
                assert isinstance(plotStyle3D, TemporalProfile3DPlotStyle)
                if plotStyle3D.isPlotable():
                    items = plotStyle3D.createPlotItem(None)
                    plotItems.extend(items)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            self.plot3D.addItems(plotItems)
            self.plot3D.updateDataRanges()
            self.plot3D.resetScaling()
            """
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    @QtCore.pyqtSlot()
    def updatePlot2D(self):
        if isinstance(self.plotSettingsModel2D, PlotSettingsModel2D):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            locations = set()
            for plotStyle in self.plotSettingsModel2D:
                assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
                if plotStyle.isPlotable():
                    locations.add(plotStyle.temporalProfile().coordinate())
Benjamin Jakimow's avatar
Benjamin Jakimow committed

                    for pdi in plotStyle.mPlotItems:
                        assert isinstance(pdi, TemporalProfilePlotDataItem)
                        pdi.updateDataAndStyle()
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
Benjamin Jakimow's avatar
Benjamin Jakimow committed

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

    def sortFilterProxyModel(self)->QSortFilterProxyModel:
        return self.mTableView.model()
    def plotSettingsModel(self)->PlotSettingsModel2D:
        return self.sortFilterProxyModel().sourceModel()
    def setItemDelegates(self, tableView):
        assert isinstance(tableView, QTableView)
        model = self.plotSettingsModel()
        assert isinstance(model, PlotSettingsModel2D)
        for c in [model.cnSensor, model.cnExpression, model.cnStyle, model.cnTemporalProfile]:
            i = model.columnNames.index(c)
            tableView.setItemDelegateForColumn(i, self)
    def getColumnName(self, index):
        assert index.isValid()
        model = self.plotSettingsModel()
        assert isinstance(model, PlotSettingsModel2D)
        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 exampleLyr(self, sensor):
        # if isinstance(sensor, SensorInstrument):
        if sensor not in self.mSensorLayers.keys():
            crs = QgsCoordinateReferenceSystem('EPSG:4862')
            uri = 'Point?crs={}'.format(crs.authid())
            lyr = QgsVectorLayer(uri, 'LOCATIONS', 'memory')
            f = sensorExampleQgsFeature(sensor)
            assert isinstance(f, QgsFeature)
            assert lyr.startEditing()
            for field in f.fields():
                lyr.addAttribute(field)
            lyr.addFeature(f)
            lyr.commitChanges()
            self.mSensorLayers[sensor] = lyr
        return self.mSensorLayers[sensor]
    def createEditor(self, parent, option, index):
        cname = self.getColumnName(index)
        model = self.plotSettingsModel()
        pmodel = self.sortFilterProxyModel()
        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
            plotStyle = model.idx2plotStyle(pmodel.mapToSource(index))
            if isinstance(plotStyle, TemporalProfile2DPlotStyle):
                if cname == model.cnExpression:
                    w = QgsFieldExpressionWidget(parent=parent)
                    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()))
                    w.setExpression(plotStyle.expression())
                    plotStyle.sigSensorChanged.connect(lambda s: w.setLayer(self.exampleLyr(s)))
                    if isinstance(plotStyle.sensor(), SensorInstrument):
                        w.setLayer(self.exampleLyr(plotStyle.sensor()))
                elif cname == model.cnStyle:
                    w = PlotStyleButton(parent=parent)
                    w.setPlotStyle(plotStyle)
                    w.setToolTip('Set style.')
                    w.sigPlotStyleChanged.connect(lambda: self.checkData(index, w, w.plotStyle()))
                elif cname == model.cnSensor:
                    w = QComboBox(parent=parent)
                    m = SensorListModel(self.mTimeSeries)
                    w.setModel(m)
                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
    def checkData(self, index, w, value):
        assert isinstance(index, QModelIndex)
        model = self.mTableView.model()
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
            plotStyle = model.idx2plotStyle(index)
            assert isinstance(plotStyle, TemporalProfile2DPlotStyle)
            if isinstance(w, QgsFieldExpressionWidget):
                assert value == w.expression()
                assert w.isExpressionValid(value) == w.isValidExpression()
                if w.isValidExpression():
                    self.commitData.emit(w)
                else:
                    s = ""
                    #print(('Delegate commit failed',w.asExpression()))
            if isinstance(w, PlotStyleButton):
                self.commitData.emit(w)
    def setEditorData(self, editor, proxyIndex):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        model = self.plotSettingsModel()
        index = self.sortFilterProxyModel().mapToSource(proxyIndex)
        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel2D):
            cname = self.getColumnName(proxyIndex)
            if cname == model.cnExpression:
                lastExpr = model.data(index, Qt.DisplayRole)
                assert isinstance(editor, QgsFieldExpressionWidget)
                editor.setProperty('lastexpr', lastExpr)
                editor.setField(lastExpr)
            elif cname == model.cnStyle:
                style = index.data()
                assert isinstance(editor, PlotStyleButton)
                editor.setPlotStyle(style)

            elif cname == model.cnSensor:
                assert isinstance(editor, QComboBox)
                m = editor.model()
                assert isinstance(m, SensorListModel)
                sensor = index.data(role=Qt.UserRole)
                if isinstance(sensor, SensorInstrument):
                    idx = m.sensor2idx(sensor)
                    editor.setCurrentIndex(idx.row())
            elif cname == model.cnTemporalProfile:
                assert isinstance(editor, QgsFeatureListComboBox)
                value = editor.identifierValue()
                if value != QVariant():
                    plotStyle = index.data(role=Qt.UserRole)
                    TP = plotStyle.temporalProfile()
                    editor.setIdentifierValue(TP.id())
                    s  = ""
            else:
                raise NotImplementedError()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def setModelData(self, w, model, proxyIndex):
        index = self.sortFilterProxyModel().mapToSource(proxyIndex)
        cname = self.getColumnName(proxyIndex)
        model = self.plotSettingsModel()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        if index.isValid() and isinstance(model, PlotSettingsModel2D):
            if cname == model.cnExpression:
                assert isinstance(w, QgsFieldExpressionWidget)
                expr = w.asExpression()
                exprLast = model.data(index, Qt.DisplayRole)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

                if w.isValidExpression() and expr != exprLast:
                    model.setData(index, w.asExpression(), Qt.EditRole)
            elif cname == model.cnStyle:
                if isinstance(w, PlotStyleButton):
                    model.setData(index, w.plotStyle(), 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):
                    TP = self.mTemporalProfileLayer.mProfiles.get(fid)
                    model.setData(index, TP, Qt.EditRole)
            else:
                raise NotImplementedError()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

class PlotSettingsModel3DWidgetDelegate(QStyledItemDelegate):
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    """
    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 = {}
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    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()))
                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()))
                elif cname == model.cnSensor:
                    w = QComboBox(parent=parent)
                    m = SensorListModel(self.mTimeSeries)
                    w.setModel(m)
                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
    def exampleLyr(self, sensor):
        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()
                if w.isValidExpression():
                    self.commitData.emit(w)
                else:
                    s = ""
                    #print(('Delegate commit failed',w.asExpression()))
            if isinstance(w, TemporalProfile3DPlotStyleButton):
                self.commitData.emit(w)
    def setEditorData(self, editor, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()
        w = None
        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            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)
            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  = ""
            else:
                raise NotImplementedError()
    def setModelData(self, w, model, index):
        cname = self.getColumnName(index)
        model = self.mTableView.model()
        if index.isValid() and isinstance(model, PlotSettingsModel3D):
            if cname == model.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)
                s = ""

            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)

            else:
                raise NotImplementedError()
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()

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

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

    l = QVBoxLayout()

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

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

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

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