Skip to content
Snippets Groups Projects
temporalprofiles.py 43.8 KiB
Newer Older
        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.mMaxProfiles = 10

        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, nMax = TP.loadingStatus()
                if nMax > 0:
                    value = '{}/{} ({:0.2f} %)'.format(nIs, nMax, float(nIs) / 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 = '{}'.format(TP.mCoordinate)
            elif columnName == self.mcnLoaded:
                nIs, nMax = TP.loadingStatus()
                value = '{}'.format(TP.mCoordinate)
        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
                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, 0)

        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 setFeatureAttribute(self, feature, name, value):
        assert isinstance(feature, QgsFeature)
        assert isinstance(name, str)
        i = feature.indexFromName(name)
        assert i >= 0, 'Field "{}" does not exist'.format(name)
        field = feature.fields()[i]
        if field.isNumeric():
            if field.type() == QVariant.Int:
                value = int(value)
            elif field.type() == QVariant.Double:
                value = float(value)
            else:
                raise NotImplementedError()
        feature.setAttribute(i, value)

    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())


    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

        return value