Skip to content
Snippets Groups Projects
temporalprofiles2d.py 53.1 KiB
Newer Older
  • Learn to ignore specific revisions
  •         self.mPlotStyle = plotStyle
            self.setAcceptedMouseButtons(Qt.LeftButton | Qt.RightButton)
            self.mPlotStyle.sigUpdated.connect(self.updateDataAndStyle)
            self.updateStyle()
    
        # On right-click, raise the context menu
        def mouseClickEvent(self, ev):
            if ev.button() == QtCore.Qt.RightButton:
                if self.raiseContextMenu(ev):
                    ev.accept()
    
        def raiseContextMenu(self, ev):
            menu = self.getContextMenus()
    
            # Let the scene add on to the end of our context menu
            # (this is optional)
            menu = self.scene().addParentContextMenus(self, menu, ev)
    
            pos = ev.screenPos()
            menu.popup(QtCore.QPoint(pos.x(), pos.y()))
            return True
    
        # This method will be called when this item's _children_ want to raise
        # a context menu that includes their parents' menus.
        def getContextMenus(self, event=None):
            if self.menu is None:
                self.menu = QMenu()
                self.menu.setTitle(self.name + " options..")
    
                green = QAction("Turn green", self.menu)
                green.triggered.connect(self.setGreen)
                self.menu.addAction(green)
                self.menu.green = green
    
                blue = QAction("Turn blue", self.menu)
                blue.triggered.connect(self.setBlue)
                self.menu.addAction(blue)
                self.menu.green = blue
    
                alpha = QWidgetAction(self.menu)
                alphaSlider = QSlider()
                alphaSlider.setOrientation(QtCore.Qt.Horizontal)
                alphaSlider.setMaximum(255)
                alphaSlider.setValue(255)
                alphaSlider.valueChanged.connect(self.setAlpha)
                alpha.setDefaultWidget(alphaSlider)
                self.menu.addAction(alpha)
                self.menu.alpha = alpha
                self.menu.alphaSlider = alphaSlider
            return self.menu
    
    
        def updateDataAndStyle(self):
    
            TP = self.mPlotStyle.temporalProfile()
            sensor = self.mPlotStyle.sensor()
    
            if isinstance(TP, TemporalProfile) and isinstance(sensor, SensorInstrument):
                x, y = TP.dataFromExpression(self.mPlotStyle.sensor(), self.mPlotStyle.expression())
    
                x = np.asarray(x, dtype=np.float)
    
                y = np.asarray(y, dtype=np.float)
                if len(y) > 0:
                    self.setData(x=x, y=y)
                else:
    
                    self.setData(x=[], y=[]) # dummy
            else:
                self.setData(x=[], y=[])  # dummy for empty data
    
        def updateStyle(self):
            """
            Updates visibility properties
            """
            from pyqtgraph.graphicsItems.ScatterPlotItem import drawSymbol
    #        path = drawSymbol(p, self.markerSymbol, self.markerSize, self.markerPen, self.markerBrush)
        #                    #painter, symbol, size, pen, brush
            self.setVisible(self.mPlotStyle.isVisible())
            self.setSymbol(self.mPlotStyle.markerSymbol)
            self.setSymbolSize(self.mPlotStyle.markerSize)
            self.setSymbolBrush(self.mPlotStyle.markerBrush)
            self.setSymbolPen(self.mPlotStyle.markerPen)
            self.setPen(self.mPlotStyle.linePen)
            self.update()
    
            #self.setPen(fn.mkPen(self.mPlotStyle.linePen))
            #self.setFillBrush(fn.mkBrush(self.mPlotStyle.mExpression))
            #self.setSymbolBrush(fn.mkBrush(self.mPlotStyle.markerBrush))
    
            # self.setFillBrush(self.mPlotStyle.)
    
            #self.update()
    
        def setClickable(self, b, width=None):
            assert isinstance(b, bool)
            self.curve.setClickable(b, width=width)
    
        def setColor(self, color):
            if not isinstance(color, QColor):
    
                color = QColor(color)
            self.setPen(color)
    
        def pen(self):
            return fn.mkPen(self.opts['pen'])
    
        def color(self):
            return self.pen().color()
    
    
        def setLineWidth(self, width):
            pen = pg.mkPen(self.opts['pen'])
            assert isinstance(pen, QPen)
            pen.setWidth(width)
            self.setPen(pen)
    
    
    
    
    class TemporalProfileLayer(QgsVectorLayer):
    
        """
        A collection to store the TemporalProfile data delivered by a PixelLoader
        """
    
        #sigSensorAdded = pyqtSignal(SensorInstrument)
        #sigSensorRemoved = pyqtSignal(SensorInstrument)
        #sigPixelAdded = pyqtSignal()
        #sigPixelRemoved = pyqtSignal()
    
        sigTemporalProfilesAdded = pyqtSignal(list)
        sigTemporalProfilesRemoved = pyqtSignal(list)
        sigMaxProfilesChanged = pyqtSignal(int)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, timeSeries, name='Temporal Profiles'):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            crs = QgsCoordinateReferenceSystem('EPSG:4326')
    
            uri = 'Point?crs={}'.format(crs.authid())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            lyrOptions = QgsVectorLayer.LayerOptions(loadDefaultStyle=False, readExtentFromXml=False)
    
            super(TemporalProfileLayer, self).__init__(uri, name, 'memory', lyrOptions)
    
            from collections import  OrderedDict
            self.mProfiles = OrderedDict()
            self.mTimeSeries = timeSeries
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #symbol = QgsFillSymbol.createSimple({'style': 'no', 'color': 'red', 'outline_color': 'black'})
            #self.mLocations.renderer().setSymbol(symbol)
    
            self.TS = None
            from timeseriesviewer.spectrallibraries import createQgsField
            fields = QgsFields()
            fields.append(createQgsField('name',''))
            fields.append(createQgsField('lon', 0.0, comment='Longitude'))
            fields.append(createQgsField('lat', 0.0, comment='Latitude'))
            fields.append(createQgsField('loaded',0, comment='Band values loaded'))
            fields.append(createQgsField('no data',0, comment='Total number of no data values'))
            fields.append(createQgsField('total',0, comment='Total number of band values'))
            fields.append(createQgsField('progress',0.0, comment='Loading progress (%)'))
            assert self.startEditing()
            assert self.dataProvider().addAttributes(fields)
            assert self.commitChanges()
            self.initConditionalStyles()
    
            self.committedFeaturesAdded.connect(self.onFeaturesAdded)
            self.committedFeaturesRemoved.connect(self.onFeaturesRemoved)
    
            return list(self.mProfiles.values())[slice]
    
        def timeSeries(self):
            return self.mTimeSeries
    
        def onFeaturesAdded(self, layerID, addedFeatures):
    
                temporalProfiles = []
                for feature in addedFeatures:
                    fid = feature.id()
                    tp = TemporalProfile(self, fid)
    
                    self.mProfiles[fid] = tp
                    temporalProfiles.append(tp)
    
                self.sigTemporalProfilesAdded.emit(temporalProfiles)
    
        def onFeaturesRemoved(self,  layerID, removedFIDs):
            if len(removedFIDs) > 0:
    
                for fid in removedFIDs:
                    toRemove.append(self.mProfiles.pop(fid))
    
                self.sigTemporalProfilesRemoved(toRemove)
    
        def initConditionalStyles(self):
            styles = self.conditionalStyles()
            assert isinstance(styles, QgsConditionalLayerStyles)
    
            for fieldName in self.fields().names():
                red = QgsConditionalStyle("@value is NULL")
                red.setTextColor(QColor('red'))
                styles.setFieldStyles(fieldName, [red])
            styles.setRowStyles([red])
    
        def createTemporalProfiles(self, coordinates):
            """
            Creates temporal profiles
            :param coordinates:
            :return:
            """
            if not isinstance(coordinates, list):
                coordinates = [coordinates]
    
            for i, coordinate in enumerate(coordinates):
                assert isinstance(coordinate, SpatialPoint)
    
                f = QgsFeature(self.fields())
                f.setGeometry(QgsGeometry.fromPointXY(coordinate.toCrs(self.crs())))
                f.setAttribute('name', 'Location {}'.format(self.mNextID))
                self.mNextID += 1
                features.append(f)
    
            b = self.isEditable()
            tps_before = list(self.mProfiles.values())
            assert self.startEditing()
            success = self.addFeatures(features)
    
            if success:
                return [tp for tp in self.mProfiles.values() if tp not in tps_before]
            else:
                return None
    
        def saveEdits(self, leaveEditable=True, triggerRepaint=True):
            """
            function to save layer changes-
            :param layer:
            :param leaveEditable:
            :param triggerRepaint:
            """
            if not self.isEditable():
                return
            if not self.commitChanges():
                self.commitErrors()
    
            if leaveEditable:
                self.startEditing()
    
            if triggerRepaint:
                self.triggerRepaint()
    
        def addMissingFields(self, fields):
            missingFields = []
            for field in fields:
                assert isinstance(field, QgsField)
                i = self.dataProvider().fieldNameIndex(field.name())
                if i == -1:
                    missingFields.append(field)
            if len(missingFields) > 0:
    
                b = self.isEditable()
                self.startEditing()
                self.dataProvider().addAttributes(missingFields)
                self.saveEdits(leaveEditable=b)
    
        def __len__(self):
            return self.featureCount()
    
        def __iter__(self):
            r = QgsFeatureRequest()
            for f in self.getFeatures(r):
                yield self.mProfiles[f.id()]
    
        def __contains__(self, item):
            return item in self.mProfiles.values()
    
    
    
        def temporalProfileToLocationFeature(self, tp:TemporalProfile):
    
            self.mLocations.selectByIds([tp.id()])
            for f in self.mLocations.selectedFeatures():
                assert isinstance(f, QgsFeature)
                return f
    
            return None
    
    
        def fromSpatialPoint(self, spatialPoint):
            """ Tests if a Temporal Profile already exists for the given spatialPoint"""
    
            spatialPoint = spatialPoint.toCrs(self.crs())
            unit = QgsUnitTypes.toAbbreviatedString(self.crs().mapUnits()).lower()
            x = spatialPoint.x() + 0.00001
            y = spatialPoint.y() + 0.
    
            if 'degree' in unit:
                dx = dy = 0.000001
    
                dx = dy = 0.1
            rect = QgsRectangle(x-dx,y-dy, x+dy,y+dy)
            for f  in self.getFeatures(rect):
                return self.mProfiles[f.id()]
            return None
    
    
        def removeTemporalProfiles(self, temporalProfiles):
            """
            Removes temporal profiles from this collection
            :param temporalProfile: TemporalProfile
            """
    
            if isinstance(temporalProfiles, TemporalProfile):
                temporalProfiles = [temporalProfiles]
            assert isinstance(temporalProfiles, list)
    
    
            temporalProfiles = [tp for tp in temporalProfiles if isinstance(tp, TemporalProfile) and tp.id() in self.mProfiles.keys()]
    
                b = self.isEditable()
                assert self.startEditing()
    
                fids = [tp.mID for tp in temporalProfiles]
    
                self.deleteFeatures(fids)
                self.saveEdits(leaveEditable=b)
    
    
                self.sigTemporalProfilesRemoved.emit(temporalProfiles)
    
    
    
        def connectTimeSeries(self, timeSeries):
            self.clear()
    
            if isinstance(timeSeries, TimeSeries):
                self.TS = timeSeries
                #for sensor in self.TS.Sensors:
                #    self.addSensor(sensor)
                #self.TS.sigSensorAdded.connect(self.addSensor)
                #self.TS.sigSensorRemoved.connect(self.removeSensor)
            else:
                self.TS = None
    
        def setMaxProfiles(self, n):
            """
            Sets the maximum number of temporal profiles to be stored in this container.
            :param n: number of profiles, must be >= 1
            """
            old = self.mMaxProfiles
    
            assert n >= 1
            if old != n:
                self.mMaxProfiles = n
    
                self.prune()
                self.sigMaxProfilesChanged.emit(self.mMaxProfiles)
    
    
        def prune(self, nMax=None):
    
            """
            Reduces the number of temporal profile to the value n defined with .setMaxProfiles(n)
            :return: [list-of-removed-TemporalProfiles]
            """
    
            if nMax is None:
                nMax = self.mMaxProfiles
    
    
            nMax = max(nMax, 1)
    
    
            toRemove = len(self) - nMax
            if toRemove > 0:
    
    
                toRemove = sorted(self[:], key=lambda p:p.id())[0:toRemove]
    
                self.removeTemporalProfiles(toRemove)
    
    
    
    
    
    
        def getFieldDefn(self, name, values):
            if isinstance(values, np.ndarray):
                # add bands
                if values.dtype in [np.int8, np.int16, np.int32, np.int64,
                                    np.uint8, np.uint16, np.uint32, np.uint64]:
                    fType = QVariant.Int
                    fTypeName = 'integer'
                elif values.dtype in [np.float16, np.float32, np.float64]:
                    fType = QVariant.Double
                    fTypeName = 'decimal'
            else:
                raise NotImplementedError()
    
            return QgsField(name, fType, fTypeName)
    
    
        def addPixelLoaderResult(self, d):
            assert isinstance(d, PixelLoaderTask)
            if d.success():
    
                for fid in d.temporalProfileIDs:
                    TP = self.mProfiles.get(fid)
    
                    if isinstance(TP, TemporalProfile):
                        TP.pullDataUpdate(d)
                    else:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        pass
    
    
        def clear(self):
            #todo: remove TS Profiles
            #self.mTemporalProfiles.clear()
            #self.sensorPxLayers.clear()
            pass
    
    
    
    class TemporalProfileTableFilterModel(QgsAttributeTableFilterModel):
    
        def __init__(self, sourceModel, parent=None):
    
            dummyCanvas = QgsMapCanvas()
            dummyCanvas.setDestinationCrs(DEFAULT_CRS)
            dummyCanvas.setExtent(QgsRectangle(-180,-90,180,90))
    
            super(TemporalProfileTableFilterModel, self).__init__(dummyCanvas, sourceModel, parent=parent)
    
    class TemporalProfileTableModel(QgsAttributeTableModel):
    
        #sigPlotStyleChanged = pyqtSignal(SpectralProfile)
        #sigAttributeRemoved = pyqtSignal(str)
        #sigAttributeAdded = pyqtSignal(str)
    
        def __init__(self, temporalProfileLayer=None, parent=None):
    
            if temporalProfileLayer is None:
                temporalProfileLayer = TemporalProfileLayer()
    
            cache = QgsVectorLayerCache(temporalProfileLayer, 1000)
    
            super(TemporalProfileTableModel, self).__init__(cache, parent)
            self.mTemporalProfileLayer = temporalProfileLayer
            self.mCache = cache
    
            assert self.mCache.layer() == self.mTemporalProfileLayer
    
            self.loadLayer()
    
        def columnNames(self):
            return self.mTemporalProfileLayer.fields().names()
    
        def feature(self, index):
    
            id = self.rowToId(index.row())
            f = self.layer().getFeature(id)
    
            return f
    
        def temporalProfile(self, index):
            feature = self.feature(index)
            return self.mTemporalProfileLayer.temporalProfileFromFeature(feature)
    
    
        def data(self, index, role=Qt.DisplayRole):
    
            """
            Returns Spectral Library data
            Use column = 0 and Qt.CheckStateRole to return PlotStyle visibility
            Use column = 0 and Qt.DecorationRole to return QIcon with PlotStyle preview
            Use column = 0 and Qt.UserRole to return entire PlotStyle
            :param index: QModelIndex
            :param role: enum Qt.ItemDataRole
            :return: value
            """
    
            if role is None or not index.isValid():
                return None
    
    
            result = super(TemporalProfileTableModel,self).data(index, role=role)
    
            return result
    
    
        def supportedDragActions(self):
            return Qt.CopyAction | Qt.MoveAction
    
        def supportedDropActions(self):
            return Qt.CopyAction | Qt.MoveAction
    
    
        def supportedDragActions(self):
            return Qt.CopyAction
    
        def supportedDropActions(self):
            return Qt.CopyAction
    
        def flags(self, index):
    
            if index.isValid():
                columnName = self.columnNames()[index.column()]
                flags = super(TemporalProfileTableModel, self).flags(index) | Qt.ItemIsSelectable
                #if index.column() == 0:
                #    flags = flags | Qt.ItemIsUserCheckable
                return flags
            return None
    
    
    class TemporalProfileFeatureSelectionManager(QgsIFeatureSelectionManager):
    
    
        def __init__(self, layer, parent=None):
            s =""
            super(TemporalProfileFeatureSelectionManager, self).__init__(parent)
            assert isinstance(layer, QgsVectorLayer)
            self.mLayer = layer
            self.mLayer.selectionChanged.connect(self.selectionChanged)
    
        def layer(self):
            return self.mLayer
    
        def deselect(self, ids):
    
            if len(ids) > 0:
                selected = [id for id in self.selectedFeatureIds() if id not in ids]
                self.mLayer.deselect(ids)
    
                self.selectionChanged.emit(selected, ids, True)
    
        def select(self, ids):
            self.mLayer.select(ids)
    
        def selectFeatures(self, selection, command):
    
            super(TemporalProfileFeatureSelectionManager, self).selectF
            s = ""
        def selectedFeatureCount(self):
            return self.mLayer.selectedFeatureCount()
    
        def selectedFeatureIds(self):
            return self.mLayer.selectedFeatureIds()
    
        def setSelectedFeatures(self, ids):
            self.mLayer.selectByIds(ids)
    
    
    
    class TemporalProfileTableView(QgsAttributeTableView):
    
        def __init__(self, parent=None):
            super(TemporalProfileTableView, self).__init__(parent)
    
    
            #self.setSelectionBehavior(QAbstractItemView.SelectRows)
            #self.setSelectionMode(QAbstractItemView.SingleSelection)
            self.horizontalHeader().setSectionsMovable(True)
            self.willShowContextMenu.connect(self.onWillShowContextMenu)
            self.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive)
    
    
            self.mSelectionManager = None
    
        def setModel(self, filterModel):
    
            super(TemporalProfileTableView, self).setModel(filterModel)
    
    
            self.mSelectionManager = TemporalProfileFeatureSelectionManager(self.model().layer())
            self.setFeatureSelectionManager(self.mSelectionManager)
            #self.selectionModel().selectionChanged.connect(self.onSelectionChanged)
    
    
        #def contextMenuEvent(self, event):
        def onWillShowContextMenu(self, menu, index):
            assert isinstance(menu, QMenu)
            assert isinstance(index, QModelIndex)
    
            featureIDs = self.spectralLibrary().selectedFeatureIds()
    
            if len(featureIDs) == 0 and index.isValid():
                if isinstance(self.model(), QgsAttributeTableFilterModel):
                    index = self.model().mapToSource(index)
                    if index.isValid():
                        featureIDs.append(self.model().sourceModel().feature(index).id())
                elif isinstance(self.model(), QgsAttributeTableFilterModel):
                    featureIDs.append(self.model().feature(index).id())
    
    
    
            if len(featureIDs) > 0:
                pass
                #m = menu.addMenu('Copy...')
                #a = m.addAction("Values")
                #a.triggered.connect(lambda b, ids=featureIDs, mode=ClipboardIO.WritingModes.VALUES: self.onCopy2Clipboard(ids, mode))
                #a = m.addAction("Attributes")
                #a.triggered.connect(lambda b, ids=featureIDs, mode=ClipboardIO.WritingModes.ATTRIBUTES: self.onCopy2Clipboard(ids, mode))
                #a = m.addAction("Values + Attributes")
                #a.triggered.connect(lambda b, ids=featureIDs, mode=ClipboardIO.WritingModes.ALL: self.onCopy2Clipboard(ids, mode))
    
            a = menu.addAction('Save as...')
            a.triggered.connect(lambda b, ids=featureIDs : self.onSaveToFile(ids))
            menu.addSeparator()
            #a = menu.addAction('Set Style')
            #a.triggered.connect(lambda b, ids=featureIDs : self.onSetStyle(ids))
            #a = menu.addAction('Check')
            #a.triggered.connect(lambda : self.setCheckState(featureIDs, Qt.Checked))
            #a = menu.addAction('Uncheck')
            #a.triggered.connect(lambda: self.setCheckState(featureIDs, Qt.Unchecked))
            #menu.addSeparator()
            for a in self.actions():
                menu.addAction(a)
    
        def temporalProfileLayer(self):
            return self.model().layer()
    
    
    
        def fidsToIndices(self, fids):
            """
            Converts feature ids into FilterModel QModelIndices
            :param fids: [list-of-int]
            :return:
            """
            if isinstance(fids, int):
                fids = [fids]
            assert isinstance(fids, list)
            fmodel = self.model()
            indices = [fmodel.fidToIndex(id) for id in fids]
            return [fmodel.index(idx.row(), 0) for idx in indices]
    
        def onRemoveFIDs(self, fids):
    
            layer = self.temporalProfileLayer()
            assert isinstance(layer, TemporalProfileLayer)
            b = layer.isEditable()
            layer.startEditing()
            layer.deleteFeatures(fids)
            layer.saveEdits(leaveEditable=b)
    
    
        def dropEvent(self, event):
            assert isinstance(event, QDropEvent)
            mimeData = event.mimeData()
    
            if self.model().rowCount() == 0:
                index = self.model().createIndex(0,0)
    
                index = self.indexAt(event.pos())
    
            #if mimeData.hasFormat(mimedata.MDF_SPECTRALLIBRARY):
             #   self.model().dropMimeData(mimeData, event.dropAction(), index.row(), index.column(), index.parent())
              #  event.accept()
    
    
    
    
    
        def dragEnterEvent(self, event):
            assert isinstance(event, QDragEnterEvent)
            #if event.mimeData().hasFormat(mimedata.MDF_SPECTRALLIBRARY):
            #    event.accept()
    
        def dragMoveEvent(self, event):
            assert isinstance(event, QDragMoveEvent)
            #if event.mimeData().hasFormat(mimedata.MDF_SPECTRALLIBRARY):
            #    event.accept()
            s = ""
    
    
        def mimeTypes(self):
            pass
    
    
    if __name__ == '__main__':
        import site, sys
        from timeseriesviewer import utils
        qgsApp = utils.initQgisApplication()
        DEBUG = False
    
    
        w.show()
    
        #btn = TemporalProfile3DPlotStyleButton()
        #btn.show()
        qgsApp.exec_()
        qgsApp.exitQgis()