Skip to content
Snippets Groups Projects
profilevisualization.py 74.8 KiB
Newer Older
  • Learn to ignore specific revisions
  •         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)