Skip to content
Snippets Groups Projects
temporalprofiles2d.py 49.5 KiB
Newer Older
  • Learn to ignore specific revisions
  •         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
            """
    
            if DEBUG:
                print('{} updateStyle'.format(self))
            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 TemporalProfileCollection(QAbstractTableModel):
        """
        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)
        def __init__(self, ):
            super(TemporalProfileCollection, self).__init__()
            #self.sensorPxLayers = dict()
            #self.memLyrCrs = QgsCoordinateReferenceSystem('EPSG:4326')
            self.newDataFlag = False
    
            self.mcnID = 'id'
            self.mcnCoordinate = 'Coordinate'
            self.mcnLoaded = 'Loading'
            self.mcnName = 'Name'
            self.mColumNames = [self.mcnName, self.mcnLoaded, self.mcnCoordinate]
    
            crs = QgsCoordinateReferenceSystem('EPSG:4862')
            uri = 'Point?crs={}'.format(crs.authid())
    
            self.TS = None
    
            self.mLocations = QgsVectorLayer(uri, 'LOCATIONS', 'memory')
    
            self.mTemporalProfiles = []
            self.mTPLookupSpatialPoint = {}
            self.mTPLookupID = {}
            self.mCurrentTPID = 0
    
    
            self.nextID = 0
    
        def __len__(self):
            return len(self.mTemporalProfiles)
    
        def __iter__(self):
            return iter(self.mTemporalProfiles)
    
        def __getitem__(self, slice):
            return self.mTemporalProfiles[slice]
    
        def __contains__(self, item):
            return item in self.mTemporalProfiles
    
        def rowCount(self, parent=None, *args, **kwargs):
            return len(self.mTemporalProfiles)
    
        def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
            return len(self.mColumNames)
    
        def idx2tp(self, index):
            if index.isValid() and index.row() < len(self.mTemporalProfiles) :
                return self.mTemporalProfiles[index.row()]
            return None
    
        def tp2idx(self, temporalProfile):
            assert isinstance(temporalProfile, TemporalProfile)
    
            if temporalProfile in self.mTemporalProfiles:
                row = self.mTemporalProfiles.index(temporalProfile)
                return self.createIndex(row, 0)
            else:
                return QModelIndex()
    
        def data(self, index, role = Qt.DisplayRole):
            if role is None or not index.isValid():
                return None
    
            value = None
            columnName = self.mColumNames[index.column()]
            TP = self.idx2tp(index)
            if not isinstance(TP, TemporalProfile):
                return None
            #self.mColumNames = ['id','coordinate','loaded']
            if role == Qt.DisplayRole:
                if columnName == self.mcnID:
                    value = TP.mID
                elif columnName == self.mcnName:
                    value = TP.name()
                elif columnName == self.mcnCoordinate:
                    value = '{}'.format(TP.mCoordinate)
                elif columnName == self.mcnLoaded:
    
                    nIs, nNoData, nMax = TP.loadingStatus()
    
                        value = '{}/{}/{} ({:0.2f} %)'.format(nIs, nNoData, nMax, float(nIs+nNoData) / nMax * 100)
    
            elif role == Qt.EditRole:
                if columnName == self.mcnName:
                    value = TP.name()
            elif role == Qt.ToolTipRole:
                if columnName == self.mcnID:
                    value = 'ID Temporal Profile'
                elif columnName == self.mcnName:
                    value = TP.name()
                elif columnName == self.mcnCoordinate:
    
                    value = 'Coordinate: {}'.format(TP.mCoordinate)
    
                elif columnName == self.mcnLoaded:
    
                    nIs, nNoData, nMax = TP.loadingStatus()
                    value = 'Band-pixels: {} loaded, {} no-data, {} total'.format(nIs, nNoData, nMax)
    
            elif role == Qt.UserRole:
                value = TP
    
            return value
    
        def flags(self, index):
            if index.isValid():
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
    
                cName = self.mColumNames[index.column()]
                if cName == self.mcnName:
                    flags = flags | Qt.ItemIsEditable
    
                return flags
                #return item.qt_flags(index.column())
            return None
    
    
        def setData(self, index, value, role=None):
            if role is None or not index.isValid():
                return None
    
            cName = self.mColumNames[index.column()]
            TP = self.idx2tp(index)
            if isinstance(TP, TemporalProfile):
                if role == Qt.EditRole and cName == self.mcnName:
                    if len(value) == 0: #do not accept empty strings
                        return False
                    else:
                        TP.setName(value)
                    return True
    
            return False
    
    
        def headerData(self, col, orientation, role):
            if Qt is None:
                return None
            if role == Qt.DisplayRole:
                if orientation == Qt.Horizontal:
                    return self.mColumNames[col]
                elif orientation == Qt.Vertical:
                    return col
            return None
    
        def insertTemporalProfiles(self, temporalProfiles, i=None):
            if isinstance(temporalProfiles, TemporalProfile):
                temporalProfiles = [temporalProfiles]
    
            assert isinstance(temporalProfiles, list)
            for temporalProfile in temporalProfiles:
                assert isinstance(temporalProfile, TemporalProfile)
    
            if i is None:
                i = len(self.mTemporalProfiles)
    
    
            temporalProfiles = [t for t in temporalProfiles if t not in self]
            l = len(temporalProfiles)
    
            if l > 0:
    
                #remove older profiles
                self.prune(nMax=self.mMaxProfiles - l)
    
                self.beginInsertRows(QModelIndex(), i, i + l - 1)
    
                for temporalProfile in temporalProfiles:
                    assert isinstance(temporalProfile, TemporalProfile)
                    id = self.nextID
                    self.nextID += 1
                    temporalProfile.mID = id
                    self.mTemporalProfiles.insert(i, temporalProfile)
                    self.mTPLookupID[id] = temporalProfile
                    self.mTPLookupSpatialPoint[temporalProfile.mCoordinate] = temporalProfile
    
                    temporalProfile.sigDataChanged.connect(lambda: self.onUpdate(temporalProfile))
                    temporalProfile.sigNameChanged.connect(lambda: self.onUpdate(temporalProfile))
    
                    i += 1
                self.endInsertRows()
    
                self.sigTemporalProfilesAdded.emit(temporalProfiles)
    
    
        def temporalProfileFromGeometry(self, geometry):
            if geometry in self.mTPLookupSpatialPoint.keys():
                return self.mTPLookupSpatialPoint[geometry]
            else:
                return None
    
        def temporalProfileFromID(self, id):
            if id in self.mTPLookupID.keys():
                return self.mTPLookupID[id]
            else:
                return None
    
        def id(self, temporalProfile):
            """
            Returns the id of an TemporalProfile
            :param temporalProfile: TemporalProfile
            :return: id or None, inf temporalProfile is not part of this collections
            """
    
            for k, tp in self.mTPLookupID.items():
                if tp == temporalProfile:
                    return k
            return None
    
        def fromID(self, id):
    
            if id in self.mTPLookupID:
    
                return self.mTPLookupID[id]
            else:
                return None
    
        def fromSpatialPoint(self, spatialPoint):
    
            if spatialPoint in self.mTPLookupSpatialPoint:
    
                return self.mTPLookupSpatialPoint[spatialPoint]
            else:
                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 in self.mTemporalProfiles]
    
            if len(temporalProfiles) > 0:
    
                def deleteFromDict(d, value):
                    assert isinstance(d, dict)
                    if value in d.values():
    
                        key = list(d.keys())[list(d.values()).index(value)]
    
                        d.pop(key)
    
                for temporalProfile in temporalProfiles:
                    assert isinstance(temporalProfile, TemporalProfile)
                    idx = self.tp2idx(temporalProfile)
                    row = idx.row()
                    self.beginRemoveRows(QModelIndex(), row, row)
                    self.mTemporalProfiles.remove(temporalProfile)
    
                    deleteFromDict(self.mTPLookupID, temporalProfile)
                    deleteFromDict(self.mTPLookupSpatialPoint,  temporalProfile)
    
                    self.endRemoveRows()
                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.mID)[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 onUpdate(self, tp):
            assert isinstance(tp, TemporalProfile)
    
            if tp in self.mTemporalProfiles:
                idx0 = self.tp2idx(tp)
                idx1 = self.createIndex(idx0.row(), self.rowCount())
                self.dataChanged.emit(idx0, idx1, [Qt.DisplayRole])
    
    
        def sort(self, col, order):
            if self.rowCount() == 0:
                return
    
            self.layoutAboutToBeChanged.emit()
            colName = self.mColumNames[col]
            r = order != Qt.AscendingOrder
    
            if colName == self.mcnName:
    
                self.mTemporalProfiles.sort(key = lambda TP:TP.name(), reverse=r)
    
            elif colName == self.mcnCoordinate:
    
                self.mTemporalProfiles.sort(key=lambda TP: str(TP.mCoordinate), reverse=r)
    
                self.mTemporalProfiles.sort(key=lambda TP: TP.mID, reverse=r)
    
                self.mTemporalProfiles.sort(key=lambda TP: TP.loadingStatus(), reverse=r)
    
            self.layoutChanged.emit()
    
    
        def addPixelLoaderResult(self, d):
            assert isinstance(d, PixelLoaderTask)
            if d.success():
    
                for TPid in d.temporalProfileIDs:
    
                    TP = self.temporalProfileFromID(TPid)
    
                    if isinstance(TP, TemporalProfile):
                        TP.pullDataUpdate(d)
                    else:
                        if DEBUG:
                            print('got result for missing TPid {}'.format(TPid))
                        s = ""
    
    
        def clear(self):
            #todo: remove TS Profiles
            #self.mTemporalProfiles.clear()
            #self.sensorPxLayers.clear()
            pass
    
    
    
    class TemporalProfileCollectionListModel(QAbstractListModel):
    
    
        def __init__(self, temporalProfileCollection, *args, **kwds):
    
            super(TemporalProfileCollectionListModel, self).__init__(*args, **kwds)
            assert isinstance(temporalProfileCollection, TemporalProfileCollection)
    
            self.mTPColl = temporalProfileCollection
            self.mTPColl.rowsAboutToBeInserted.connect(self.rowsAboutToBeInserted)
            self.mTPColl.rowsInserted.connect(self.rowsInserted.emit)
            #self.mTPColl.rowsAboutToBeRemoved.connect(self.rowsAboutToBeRemoved)
            self.mTPColl.rowsRemoved.connect(lambda : self.modelReset.emit())
    
            self.mTPColl.dataChanged.connect(self.onDataChanged)
    
        def onDataChanged(self, idx0, idx1, roles):
            idx0r = self.createIndex(idx0.row(), idx0.column())
            idx1r = self.createIndex(idx1.row(), idx1.column())
    
            tp = self.idx2tp(idx0r)
            assert isinstance(tp, TemporalProfile)
            self.dataChanged.emit(idx0r, idx1r, roles)
    
    
    
        def idx2tp(self, *args, **kwds):
            return self.mTPColl.idx2tp(*args, **kwds)
    
        def tp2idx(self, *args, **kwds):
            return self.mTPColl.tp2idx(*args, **kwds)
    
        def flags(self, index):
            if index.isValid():
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
                return flags
                #return item.qt_flags(index.column())
            return Qt.NoItemFlags
    
        def rowCount(self, *args, **kwds):
            return self.mTPColl.rowCount(*args, **kwds)
    
    
        def data(self, index, role=Qt.DisplayRole):
            if role is None or not index.isValid():
                return None
    
    
    
            TP = self.mTPColl.idx2tp(index)
            value = None
            if isinstance(TP, TemporalProfile):
                if role == Qt.DisplayRole:
                    value = '{}'.format(TP.name())
                elif role == Qt.ToolTipRole:
                    value = '#{} "{}" {}'.format(TP.mID, TP.name(), TP.mCoordinate)
                elif role == Qt.UserRole:
                    value = TP
    
            else:
                if role == Qt.DisplayRole:
                    value = 'undefined'
                elif role == Qt.ToolTipRole:
                    value = 'Please select a location to read the temporal profile from'
    
    
    if __name__ == '__main__':
        import site, sys
        from timeseriesviewer import utils
        qgsApp = utils.initQgisApplication()
        DEBUG = False
    
        w = TemporalProfilePlotStyle3DWidget()
        w.show()
        print(w.plotStyle())
    
        #btn = TemporalProfile3DPlotStyleButton()
        #btn.show()
        qgsApp.exec_()
        qgsApp.exitQgis()