Skip to content
Snippets Groups Projects
temporalprofiles2d.py 56 KiB
Newer Older
  • Learn to ignore specific revisions
  •     #sigSensorRemoved = pyqtSignal(SensorInstrument)
        #sigPixelAdded = pyqtSignal()
        #sigPixelRemoved = pyqtSignal()
    
        sigTemporalProfilesAdded = pyqtSignal(list)
        sigTemporalProfilesRemoved = pyqtSignal(list)
        sigMaxProfilesChanged = pyqtSignal(int)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, timeSeries, name='Temporal Profiles'):
            assert isinstance(timeSeries, TimeSeries)
    
    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)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            fields.append(createQgsField(FN_ID, self.mNextID))
            fields.append(createQgsField(FN_NAME,''))
            fields.append(createQgsField(FN_X, 0.0, comment='Longitude'))
            fields.append(createQgsField(FN_Y, 0.0, comment='Latitude'))
            fields.append(createQgsField(FN_N_TOTAL, 0, comment='Total number of band values'))
            fields.append(createQgsField(FN_N_NODATA,0, comment='Total of no-data values.'))
            fields.append(createQgsField(FN_N_LOADED, 0, comment='Loaded valid band values.'))
            fields.append(createQgsField(FN_N_LOADED_PERCENT,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]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def loadMissingData(self, backgroundProcess=False):
            assert isinstance(self.mTimeSeries, TimeSeries)
    
            # Get or create the TimeSeriesProfiles which will store the loaded values
    
    
            tasks = []
    
            theGeometries = []
    
            # Define which (new) bands need to be loaded for each sensor
            LUT_bandIndices = dict()
            for sensor in self.mTimeSeries.Sensors:
                    LUT_bandIndices[sensor] = list(range(sensor.nb))
    
            PL = PixelLoader()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            PL.sigPixelLoaded.connect(self.addPixelLoaderResult)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # update new / existing points
    
            for tsd in self.mTimeSeries:
                assert isinstance(tsd, TimeSeriesDatum)
    
    
                requiredIndices = LUT_bandIndices[tsd.sensor]
                requiredIndexKeys = [bandIndex2bandKey(b) for b in requiredIndices]
                TPs = []
                missingIndices = set()
                for TP in self.mProfiles.values():
                    assert isinstance(TP, TemporalProfile)
                    dataValues = TP.mData[tsd]
                    existingKeys = list(dataValues.keys())
                    missingIdx = [bandKey2bandIndex(k) for k in requiredIndexKeys if k not in existingKeys]
                    if len(missingIdx) > 0:
                        TPs.append(TP)
                        missingIndices.union(set(missingIdx))
    
                if len(TPs) > 0:
                    theGeometries = [tp.coordinate() for tp in TPs]
                    theIDs = [tp.id() for tp in TPs]
                    task = PixelLoaderTask(tsd.pathImg, theGeometries,
                                           bandIndices=requiredIndices,
                                           temporalProfileIDs=theIDs)
                    tasks.append(task)
    
    
            if len(tasks) > 0:
    
    
                # self.pixelLoader.setNumberOfProcesses(SETTINGS.value('profileloader_threads', aGoodDefault))
                if backgroundProcess:
                    PL.startLoading(tasks)
                else:
                    import timeseriesviewer.pixelloader
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    tasks = [PixelLoaderTask.fromDump(timeseriesviewer.pixelloader.doLoaderTask(None, task.toDump())) for task in tasks]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    l = len(tasks)
                    for i, task in enumerate(tasks):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        PL.sigPixelLoaded.emit(task)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            else:
                if DEBUG:
                    print('Data for geometries already loaded')
    
            s = ""
    
        def saveTemporalProfiles(self, pathVector, loadMissingValues=False, sep='\t'):
            if pathVector is None or len(pathVector) == 0:
                global DEFAULT_SAVE_PATH
                if DEFAULT_SAVE_PATH == None:
                    DEFAULT_SAVE_PATH = 'temporalprofiles.shp'
                d = os.path.dirname(DEFAULT_SAVE_PATH)
                filters = QgsProviderRegistry.instance().fileVectorFilters()
                pathVector, filter = QFileDialog.getSaveFileName(None, 'Save {}'.format(self.name()), DEFAULT_SAVE_PATH,
                                                                 filter=filters)
    
                if len(pathVector) == 0:
                    return None
                else:
                    DEFAULT_SAVE_PATH = pathVector
    
            if loadMissingValues:
                self.loadMissingData(backgroundProcess=False)
                for p in self.mProfiles.values():
                    assert isinstance(p, TemporalProfile)
                    p.loadMissingData()
    
            drvName = QgsVectorFileWriter.driverForExtension(os.path.splitext(pathVector)[-1])
            QgsVectorFileWriter.writeAsVectorFormat(self, pathVector, 'utf-8', destCRS=self.crs(), driverName=drvName)
    
            pathCSV = os.path.splitext(pathVector)[0] + '.data.csv'
            # write a flat list of profiles
            lines = ['Temporal Profiles']
            nBands = max([s.nb for s in self.mTimeSeries.sensors()])
            lines.append(sep.join(['id', 'name', 'sensor', 'date', 'doy', 'sensor'] + ['b{}'.format(b+1) for b in range(nBands)]))
    
            for p in list(self.getFeatures()):
    
                assert isinstance(p, QgsFeature)
                fid = p.id()
                tp = self.mProfiles.get(fid)
                if tp is None:
                    continue
                assert isinstance(tp, TemporalProfile)
                name = tp.name()
                for tsd, values in tp.mData.items():
                    assert isinstance(tsd, TimeSeriesDatum)
                    line = [fid, name, tsd.sensor.name(), tsd.date, tsd.doy]
                    for b in range(tsd.sensor.nb):
                        key = 'b{}'.format(b+1)
                        line.append(values.get(key))
    
                    line = ['' if v == None else str(v) for v in line]
                    line = sep.join([str(l) for l in line])
                    lines.append(line)
                s = ""
    
    
    
                file = open(pathCSV, 'w', encoding='utf8')
                file.writelines('\n'.join(lines))
                #print('\n'.join(lines))
                file.flush()
                file.close()
    
            return [pathVector, pathCSV]
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Returns the TimeSeries instance.
            :return: TimeSeries
            """
    
        def onFeaturesAdded(self, layerID, addedFeatures):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Create a TemporalProfile object for each QgsFeature added to the backend QgsVectorLayer
            :param layerID:
            :param addedFeatures:
            :return:
            """
    
                temporalProfiles = []
                for feature in addedFeatures:
                    fid = feature.id()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    if fid < 0:
                        continue
    
                    self.mProfiles[fid] = tp
                    temporalProfiles.append(tp)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if len(temporalProfiles) > 0:
                    pass
                    #self.sigTemporalProfilesAdded.emit(temporalProfiles)
    
        def onFeaturesRemoved(self,  layerID, removedFIDs):
            if len(removedFIDs) > 0:
    
                for fid in removedFIDs:
                    toRemove.append(self.mProfiles.pop(fid))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                self.sigTemporalProfilesRemoved.emit(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])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def createTemporalProfiles(self, coordinates)->list:
    
            """
            Creates temporal profiles
            :param coordinates:
            :return:
            """
            if not isinstance(coordinates, list):
                coordinates = [coordinates]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            n = self.dataProvider().featureCount()
    
            for i, coordinate in enumerate(coordinates):
                assert isinstance(coordinate, SpatialPoint)
    
                f = QgsFeature(self.fields())
                f.setGeometry(QgsGeometry.fromPointXY(coordinate.toCrs(self.crs())))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                f.setAttribute(FN_ID, self.mNextID)
                f.setAttribute(FN_NAME, 'Location {}'.format(self.mNextID))
                f.setAttribute(FN_X, coordinate.x())
                f.setAttribute(FN_Y, coordinate.y())
                f.setAttribute(FN_N_LOADED_PERCENT, 0.0)
                f.setAttribute(FN_N_LOADED, 0)
                f.setAttribute(FN_N_TOTAL, 0)
                f.setAttribute(FN_N_NODATA, 0)
    
            b = self.isEditable()
            tps_before = list(self.mProfiles.values())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.startEditing()
    
            success = self.addFeatures(features)
            self.saveEdits(leaveEditable=b)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                assert n+len(features) == self.dataProvider().featureCount()
                assert self.dataProvider().featureCount() == len(self.mProfiles)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                profiles = [tp for tp in self.mProfiles.values() if tp not in tps_before]
                for p in profiles:
                    p.updateLoadingStatus()
                return profiles
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                return []
    
        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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.dataProvider().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"""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            for p in list(self.mProfiles.values()):
                assert isinstance(p, TemporalProfile)
                if p.coordinate() == spatialPoint:
                    return p
            """
    
            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()]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
    
    
        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 loadCoordinatesFromOgr(self, path):
            """Loads the TemporalProfiles for vector geometries in data source 'path' """
            if path is None:
                filters = QgsProviderRegistry.instance().fileVectorFilters()
                defDir = None
                if isinstance(DEFAULT_SAVE_PATH, str) and len(DEFAULT_SAVE_PATH) > 0:
                    defDir = os.path.dirname(DEFAULT_SAVE_PATH)
                path, filter = QFileDialog.getOpenFileName(directory=defDir, filter=filters)
    
            if isinstance(path, str) and len(path) > 0:
                sourceLyr = QgsVectorLayer(path)
                nameAttribute = None
    
                fieldNames = [n.lower() for n in sourceLyr.fields().names()]
                for candidate in ['name', 'id']:
                    if candidate in fieldNames:
                        nameAttribute = sourceLyr.fields().names()[fieldNames.index(candidate)]
                        break
    
                if len(self.timeSeries()) == 0:
                    sourceLyr.selectAll()
                else:
                    extent = self.timeSeries().getMaxSpatialExtent(sourceLyr.crs())
                    sourceLyr.selectByRect(extent)
                newProfiles = []
                for feature in sourceLyr.selectedFeatures():
                    assert isinstance(feature, QgsFeature)
                    geom = feature.geometry()
                    if isinstance(geom, QgsGeometry):
                        point = geom.centroid().constGet()
                        try:
                            TPs = self.createTemporalProfiles(SpatialPoint(sourceLyr.crs(), point))
                            for TP in TPs:
                                if nameAttribute:
                                    name = feature.attribute(nameAttribute)
                                else:
                                    name = 'FID {}'.format(feature.id())
                                TP.setName(name)
                                newProfiles.append(TP)
                        except Exception as ex:
                            print(ex)
    
    
        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)
    
        AUTOGENERATES_COLUMNS = [FN_Y, FN_X, FN_N_LOADED, FN_N_TOTAL, FN_N_NODATA, FN_ID, FN_N_LOADED_PERCENT]
    
    
        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 Temporal Profile Layer values
    
            :param index: QModelIndex
            :param role: enum Qt.ItemDataRole
            :return: value
            """
    
            if role is None or not index.isValid():
                return None
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            result = super(TemporalProfileTableModel, self).data(index, role=role)
    
    
        def setData(self, index, value, role=None):
            """
            Sets Temporal Profile Data.
            :param index: QModelIndex()
            :param value: value to set
            :param role: role
            :return: True | False
            """
            if role is None or not index.isValid():
                return False
    
            f = self.feature(index)
            result = False
    
            if value == None:
                value = QVariant()
            cname = self.columnNames()[index.column()]
            if role == Qt.EditRole and cname not in TemporalProfileTableModel.AUTOGENERATES_COLUMNS:
                i = f.fieldNameIndex(cname)
                if f.attribute(i) == value:
                    return False
                b = self.mTemporalProfileLayer.isEditable()
                self.mTemporalProfileLayer.startEditing()
                self.mTemporalProfileLayer.changeAttributeValue(f.id(), i, value)
                self.mTemporalProfileLayer.saveEdits(leaveEditable=b)
                result = True
                #f = self.layer().getFeature(profile.id())
                #i = f.fieldNameIndex(SpectralProfile.STYLE_FIELD)
                #self.layer().changeAttributeValue(f.id(), i, value)
                #result = super().setData(self.index(index.row(), self.mcnStyle), value, role=Qt.EditRole)
                #if not b:
                #    self.layer().commitChanges()
            if result:
                self.dataChanged.emit(index, index, [role])
            else:
                result = super().setData(index, value, role=role)
    
    
            return result
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def headerData(self, section:int, orientation:Qt.Orientation, role:int):
            data = super(TemporalProfileTableModel, self).headerData(section, orientation, role)
            if role == Qt.ToolTipRole and orientation == Qt.Horizontal:
                #add the field comment to column description
                field = self.layer().fields().at(section)
                assert isinstance(field, QgsField)
                comment = field.comment()
                if len(comment) > 0:
                    data = re.sub('</p>$', ' <i>{}</i></p>'.format(comment), data)
    
            return data
    
    
        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
    
    
                if columnName in TemporalProfileTableModel.AUTOGENERATES_COLUMNS:
                    flags = flags ^ Qt.ItemIsEditable
    
                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)
    
            self.mContextMenuActions = []
    
        def setContextMenuActions(self, actions:list):
            self.mContextMenuActions = actions
    
    
        #def contextMenuEvent(self, event):
        def onWillShowContextMenu(self, menu, index):
            assert isinstance(menu, QMenu)
            assert isinstance(index, QModelIndex)
    
    
            featureIDs = self.temporalProfileLayer().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())
    
    
            for a in self.mContextMenuActions:
                menu.addAction(a)
    
    
            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