Skip to content
Snippets Groups Projects
timeseries.py 61 KiB
Newer Older
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def saveToFile(self, path):
        """
        Saves the TimeSeries sources into a CSV file
        :param path: str, path of CSV file
        :return: path of CSV file
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if path is None or len(path) == 0:
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
            return None
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        lines = []
        lines.append('#Time series definition file: {}'.format(np.datetime64('now').astype(str)))
        lines.append('#<image path>')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for TSD in self.mTSDs:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            assert isinstance(TSD, TimeSeriesDatum)
            for pathImg in TSD.sourceUris():
                lines.append(pathImg)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        lines = [l+'\n' for l in lines]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        with open(path, 'w') as f:
            f.writelines(lines)
            messageLog('Time series source images written to {}'.format(path))

benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        return path
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def pixelSizes(self):
        """
        Returns the pixel sizes of all SensorInstruments
        :return: [list-of-QgsRectangles]
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for sensor in self.mSensors2TSDs.keys():
            r.append((QgsRectangle(sensor.px_size_x, sensor.px_size_y)))
        return r

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def maxSpatialExtent(self, crs=None)->SpatialExtent:
        """
        Returns the maximum SpatialExtent of all images of the TimeSeries
        :param crs: QgsCoordinateSystem to express the SpatialExtent coordinates.
        :return:
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        extent = None
        for i, tsd in enumerate(self.mTSDs):
            assert isinstance(tsd, TimeSeriesDatum)
            ext = tsd.spatialExtent()
            if isinstance(extent, SpatialExtent):
                extent = extent.combineExtentWith(ext)
            else:
                extent = ext
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        return extent
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        """
        Returns the TimeSeriesDatum related to an image source
        :param pathOfInterest: str, image source uri
        :return: TimeSeriesDatum
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for tsd in self.mTSDs:
            assert isinstance(tsd, TimeSeriesDatum)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if pathOfInterest in tsd.sourceUris():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def tsd(self, date: np.datetime64, sensor)->TimeSeriesDatum:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        """
        Returns the TimeSeriesDatum identified by date and sensorID
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        :param date:
        :param sensor: SensorInstrument | str with sensor id
        :return:
        """
        assert isinstance(date, np.datetime64)
        if isinstance(sensor, str):
            sensor = self.sensor(sensor)
        if isinstance(sensor, SensorInstrument):
            for tsd in self.mTSDs:
                if tsd.date() == date and tsd.sensor() == sensor:
                    return tsd
        return None

    def insertTSD(self, tsd: TimeSeriesDatum)->TimeSeriesDatum:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        """
        Inserts a TimeSeriesDatum
        :param tsd: TimeSeriesDatum
        """
        #insert sorted by time & sensor
        assert tsd not in self.mTSDs
        assert tsd.sensor() in self.mSensors
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        tsd.mTimeSeries = self
        tsd.sigRemoveMe.connect(lambda: self.removeTSDs([tsd]))
        tsd.rowsAboutToBeRemoved.connect(self.onSourcesAboutToBeRemoved)
        tsd.rowsRemoved.connect(self.onSourcesRemoved)
        tsd.rowsAboutToBeInserted.connect(self.onSourcesAboutToBeInserted)
        tsd.rowsInserted.connect(self.onSourcesInserted)

        row = bisect.bisect(self.mTSDs, tsd)
        self.beginInsertRows(self.mRootIndex, row, row)
        self.mTSDs.insert(row, tsd)
        self.endInsertRows()
        #self.rowsInserted()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        return tsd

    def onSourcesAboutToBeRemoved(self, parent, first, last):
        s = ""
        pass

    def onSourcesRemoved(self, parent, first, last):
        s = ""
    
    def onSourcesAboutToBeInserted(self, parent, first, last):
        s = ""
        
    def onSourcesInserted(self, parent, first, last):
        s = ""
        
  
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def removeTSDs(self, tsds):
        """
        Removes a list of TimeSeriesDatum
        :param tsds: [list-of-TimeSeriesDatum]
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        removed = list()
        for tsd in tsds:
            assert isinstance(tsd, TimeSeriesDatum)
            row = self.mTSDs.index(tsd)
            self.beginRemoveRows(self.mRootIndex, row, row)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.mTSDs.remove(tsd)
            tsd.mTimeSeries = None
            removed.append(tsd)
        if len(removed) > 0:
            self.sigTimeSeriesDatesRemoved.emit(removed)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def tsds(self, date:np.datetime64=None, sensor:SensorInstrument=None)->list:

        """
        Returns a list of  TimeSeriesDatum of the TimeSeries. By default all TimeSeriesDatum will be returned.
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        :param date: numpy.datetime64 to return the TimeSeriesDatum for
        :param sensor: SensorInstrument of interest to return the [list-of-TimeSeriesDatum] for.
        :return: [list-of-TimeSeriesDatum]
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        tsds = self.mTSDs[:]
        if date:
            tsds = [tsd for tsd in tsds if tsd.date() == date]
        if sensor:
            tsds = [tsd for tsd in tsds if tsd.sensor() == sensor]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return tsds

    def clear(self):
        """
        Removes all data sources from the TimeSeries (which will be empty after calling this routine).
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.removeTSDs(self[:])

Benjamin Jakimow's avatar
Benjamin Jakimow committed


    def addSensor(self, sensor:SensorInstrument):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        Adds a Sensor
        :param sensor: SensorInstrument
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if not sensor in self.mSensors:
            self.mSensors.append(sensor)
            self.sigSensorAdded.emit(sensor)
            return sensor
        else:
            return None

    def checkSensorList(self):
        """
        Removes sensors without linked TSD / no data
        """
        to_remove = []
        for sensor in self.sensors():
            tsds = [tsd for tsd in self.mTSDs if tsd.sensor() == sensor]
            if len(tsds) == 0:
                to_remove.append(sensor)
        for sensor in to_remove:
            self.removeSensor(sensor)

    def removeSensor(self, sensor:SensorInstrument)->SensorInstrument:
        """
        Removes a sensor and all linked images
        :param sensor: SensorInstrument
        :return: SensorInstrument or none, if sensor was not defined in the TimeSeries
        """
        assert isinstance(sensor, SensorInstrument)
        if sensor in self.mSensors:
            tsds = [tsd for tsd in self.mTSDs if tsd.sensor() == sensor]
            self.removeTSDs(tsds)
            self.mSensors.remove(sensor)
            self.sigSensorRemoved.emit(sensor)
            return sensor
        return None
    def addSources(self, sources:list, progressDialog:QProgressDialog=None):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        Adds new data sources to the TimeSeries
        :param sources: [list-of-TimeSeriesSources]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(sources, list)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        nMax = len(sources)
        #self.sigTimeSeriesSourcesAboutToBeChanged.emit()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        self.sigLoadingProgress.emit(0, nMax, 'Start loading {} sources...'.format(nMax))
        
        if isinstance(progressDialog, QProgressDialog):
            progressDialog.setRange(0, nMax)
            progressDialog.setLabelText('Load rasters...'.format(nMax))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        # 1. read sources
        # this could be excluded into a parallel process
        addedDates = []
        for i, source in enumerate(sources):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            msg = None
            if False: #debug
                newTSD = self._addSource(source)
            else:
                try:
                    newTSD = self._addSource(source)
                except Exception as ex:
                    msg = 'Unable to add: {}\n{}'.format(str(source), str(ex))
                    print(msg, file=sys.stderr)

            if isinstance(progressDialog, QProgressDialog):
                if progressDialog.wasCanceled():
                    break
                progressDialog.setValue(i)
                progressDialog.setLabelText('{}/{}'.format(i+1, nMax))

                self.sigLoadingProgress.emit(i+1, nMax, msg)
            if isinstance(newTSD, TimeSeriesDatum):
                addedDates.append(newTSD)
        #if len(addedDates) > 0:
        if isinstance(progressDialog, QProgressDialog):
            progressDialog.setLabelText('Create map widgets...')

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if len(addedDates) > 0:
            self.sigTimeSeriesDatesAdded.emit(addedDates)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def _addSource(self, source:TimeSeriesSource)->TimeSeriesDatum:
        """
        :param source:
        :return: TimeSeriesDatum (if new created)
        """
        if isinstance(source, TimeSeriesSource):
            tss = source
        else:
            tss = TimeSeriesSource.create(source)

        assert isinstance(tss, TimeSeriesSource)

        newTSD = None

        tss.mDate = self.date2date(tss.date())
        date = tss.date()
        sid = tss.sid()
        sensor = self.sensor(sid)
        # if necessary, add a new sensor instance
        if not isinstance(sensor, SensorInstrument):
            sensor = self.addSensor(SensorInstrument(sid))
        assert isinstance(sensor, SensorInstrument)
        tsd = self.tsd(date, sensor)
        # if necessary, add a new TimeSeriesDatum instance
        if not isinstance(tsd, TimeSeriesDatum):
            tsd = self.insertTSD(TimeSeriesDatum(self, date, sensor))
            newTSD = tsd
            # addedDates.append(tsd)
        assert isinstance(tsd, TimeSeriesDatum)
        # add the source
        tsd.addSource(tss)
        return newTSD

    def setDateTimePrecision(self, mode:DateTimePrecision):
        """
        Sets the precision with which the parsed DateTime information will be handled.
        :param mode: TimeSeriesViewer:DateTimePrecision
        :return:
        """
        self.mDateTimePrecision = mode

        #do we like to update existing sources?




    def date2date(self, date:np.datetime64):
        assert isinstance(date, np.datetime64)
        if self.mDateTimePrecision == DateTimePrecision.Original:
            return date
        else:
            date = np.datetime64(date, self.mDateTimePrecision.value)

        return date

    def sources(self) -> list:
        """
        Returns the input sources
        :return: iterator over [list-of-TimeSeriesSources]
        """

        for tsd in self:
            for source in tsd:
                yield source


Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def sourceUris(self)->list:
        """
        Returns the uris of all sources
        :return: [list-of-str]
        """
        uris = []
        for tsd in self:
            assert isinstance(tsd, TimeSeriesDatum)
            uris.extend(tsd.sourceUris())
        return uris

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return len(self.mTSDs)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return iter(self.mTSDs)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def __getitem__(self, slice):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return self.mTSDs[slice]

    def __delitem__(self, slice):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.removeTSDs(slice)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def __contains__(self, item):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return item in self.mTSDs
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def __repr__(self):
        info = []
        info.append('TimeSeries:')
        l = len(self)
        info.append('  Scenes: {}'.format(l))

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        return '\n'.join(info)

    def headerData(self, section, orientation, role):
        assert isinstance(section, int)

        if orientation == Qt.Horizontal and role == Qt.DisplayRole:

            if len(self.mColumnNames) > section:
                return self.mColumnNames[section]
            else:
                return ''

        else:
            return None

    def parent(self, index: QModelIndex) -> QModelIndex:
        """
        Returns the parent index of a QModelIndex `index`
        :param index: QModelIndex
        :return: QModelIndex
        """
        if not index.isValid():
            return QModelIndex()

        node = index.internalPointer()
        tsd = None
        tss = None

        if isinstance(node, TimeSeriesDatum):
            return self.mRootIndex

        elif isinstance(node, TimeSeriesSource):
            tss = node
            tsd = node.timeSeriesDatum()
            return self.createIndex(self.mTSDs.index(tsd), 0, tsd)

    def rowCount(self, index: QModelIndex=None) -> int:
        """
        Return the row-count, i.e. number of child node for a TreeNode as index `index`.
        :param index: QModelIndex
        :return: int
        """
        if index is None:
            index = QModelIndex()

        if not index.isValid():
            return len(self)

        node = index.internalPointer()
        if isinstance(node, TimeSeriesDatum):
            return len(node)

        if isinstance(node, TimeSeriesSource):
            return 0


    def columnNames(self) -> list:
        """
        Returns the column names
        :return: [list-of-string]
        """
        return self.mColumnNames[:]

    def columnCount(self, index:QModelIndex = None) -> int:
        """
        Returns the number of columns
        :param index: QModelIndex
        :return:
        """

        return len(self.mColumnNames)


    def connectTreeView(self, treeView):
        self.mTreeView = treeView

    def index(self, row: int, column: int, parent: QModelIndex = None) -> QModelIndex:
        """
        Returns the QModelIndex
        :param row: int
        :param column: int
        :param parent: QModelIndex
        :return: QModelIndex
        """
        if parent is None:
            parent = self.mRootIndex
        else:
            assert isinstance(parent, QModelIndex)

        if row < 0 or row >= len(self):
            return QModelIndex()
        if column < 0 or column >= len(self.mColumnNames):
            return QModelIndex()


        if parent == self.mRootIndex:
            # TSD node
            if row < 0 or row >= len(self):
                return QModelIndex()
            return self.createIndex(row, column, self[row])

        elif parent.parent() == self.mRootIndex:
            # TSS node
            tsd = self.tsdFromIdx(parent)
            if row < 0 or row >= len(tsd):
                return QModelIndex()
            return self.createIndex(row, column, tsd[row])

        return QModelIndex()

    def tsdToIdx(self, tsd:TimeSeriesDatum)->QModelIndex:
        """
        Returns an QModelIndex pointing on a TimeSeriesDatum of interest
        :param tsd: TimeSeriesDatum
        :return: QModelIndex
        """
        row = self.mTSDs.index(tsd)
        return self.index(row, 0)

    def tsdFromIdx(self, index: QModelIndex) -> TimeSeriesDatum:
        """
        Returns the TimeSeriesDatum related to an QModelIndex `index`.
        :param index: QModelIndex
        :return: TreeNode
        """

        if index.row() == -1 and index.column() == -1:
            return None
        elif not index.isValid():
            return None
        else:
            node = index.internalPointer()
            if isinstance(node, TimeSeriesDatum):
                return node
            elif isinstance(node, TimeSeriesSource):
                return node.timeSeriesDatum()

        return None

    def data(self, index, role):
        """

        :param index: QModelIndex
        :param role: Qt.ItemRole
        :return: object
        """
        assert isinstance(index, QModelIndex)
        if not index.isValid():
            return None

        node = index.internalPointer()
        tsd = None
        tss = None
        if isinstance(node, TimeSeriesSource):
            tsd = node.timeSeriesDatum()
            tss = node
        elif isinstance(node, TimeSeriesDatum):
            tsd = node

        if role == Qt.UserRole:
            return node

        cName = self.mColumnNames[index.column()]

        if isinstance(tss, TimeSeriesSource):
            if role in [Qt.DisplayRole]:
                if cName == self.cnDate:
                    return str(tsd.date())
                if cName == self.cnImages:
                    return tss.uri()
                if cName == self.cnNB:
                    return tss.nb
                if cName == self.cnNL:
                    return tss.nl
                if cName == self.cnNS:
                    return tss.ns
                if cName == self.cnCRS:
                    return tss.crs().description()

        if isinstance(tsd, TimeSeriesDatum):
            if role in [Qt.DisplayRole]:
                if cName == self.cnSensor:
                    return tsd.sensor().name()
                if cName == self.cnImages:
                    return len(tsd)
                if cName == self.cnDate:
                    return str(tsd.date())

            if role == Qt.BackgroundColorRole and tsd in self.mCurrentDates:
                return QColor('yellow')

        if isinstance(node, TimeSeriesDatum) and index.column() == 0:
            if role == Qt.CheckStateRole:
                return Qt.Checked if tsd.isVisible() else Qt.Unchecked



        return None

    def setData(self, index: QModelIndex, value: typing.Any, role: int):

        if not index.isValid():
            return False

        result = False

        node = index.internalPointer()
        if isinstance(node, TimeSeriesDatum):
            if role == Qt.CheckStateRole and index.column() == 0:
                node.setVisibility(value == Qt.Checked)
                result = True

        if result == True:
            self.dataChanged.emit(index, index, [role])

        return result

    def flags(self, index):
        assert isinstance(index, QModelIndex)
        if not index.isValid():
            return Qt.NoItemFlags
        #cName = self.mColumnNames.index(index.column())
        flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
        if isinstance(index.internalPointer(), TimeSeriesDatum) and index.column() == 0:
            flags = flags | Qt.ItemIsUserCheckable
        return flags


class TimeSeriesTableModel(QAbstractTableModel):

    def __init__(self, TS:TimeSeries, parent=None, *args):

        super(TimeSeriesTableModel, self).__init__()
        assert isinstance(TS, TimeSeries)
        self.cnDate = 'Date'
        self.cnSensor = 'Sensor'
        self.cnNS = 'ns'
        self.cnNL = 'nl'
        self.cnNB = 'nb'
        self.cnCRS = 'CRS'
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.cnImages = 'Image(s)'
        self.mColumnNames = [self.cnDate, self.cnSensor,
                             self.cnNS, self.cnNL, self.cnNB,
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                             self.cnCRS, self.cnImages]
        self.mTimeSeries = TS
        self.mSensors = set()
        self.mTimeSeries.sigTimeSeriesDatesRemoved.connect(self.removeTSDs)
        self.mTimeSeries.sigTimeSeriesDatesAdded.connect(self.addTSDs)

        self.items = []
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        self.addTSDs([tsd for tsd in self.mTimeSeries])
    def timeSeries(self)->TimeSeries:
        """
        :return: TimeSeries
        """
        return self.mTimeSeries

    def removeTSDs(self, tsds:list):
        """
        Removes TimeSeriesDatum instances
        :param tsds: list
        """
        for tsd in tsds:
            if tsd in self.mTimeSeries:
                self.mTimeSeries.removeTSDs([tsd])
            elif tsd in self.items:
                idx = self.getIndexFromDate(tsd)
                self.removeRows(idx.row(), 1)

        idx = self.getIndexFromDate(tsd)
        self.dataChanged.emit(idx, idx)

    def sensorsChanged(self, sensor):
        i = self.mColumnNames.index(self.cnSensor)
        idx0 = self.createIndex(0, i)
        idx1 = self.createIndex(self.rowCount(), i)
        self.dataChanged.emit(idx0, idx1)

    def addTSDs(self, tsds):

        for tsd in tsds:
            assert isinstance(tsd, TimeSeriesDatum)
            row = bisect.bisect_left(self.items, tsd)
            self.beginInsertRows(QModelIndex(), row, row)
            self.items.insert(row, tsd)
            self.endInsertRows()

            #self.sort(self.sortColumnIndex, self.sortOrder)

        for tsd in tsds:
            assert isinstance(tsd, TimeSeriesDatum)
            tsd.sigVisibilityChanged.connect(lambda: self.tsdChanged(tsd))

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for sensor in set([tsd.sensor() for tsd in tsds]):
            if sensor not in self.mSensors:
                self.mSensors.add(sensor)
                sensor.sigNameChanged.connect(self.sensorsChanged)
    def rowCount(self, parent = QModelIndex())->int:
        return len(self.items)


    def removeRows(self, row, count , parent=QModelIndex()):
        self.beginRemoveRows(parent, row, row+count-1)
        toRemove = self.items[row:row+count]
        for tsd in toRemove:
            self.items.remove(tsd)
        self.endRemoveRows()

    def getIndexFromDate(self, tsd:TimeSeriesDatum)->QModelIndex:
        assert isinstance(tsd, TimeSeriesDatum)
        return self.createIndex(self.items.index(tsd),0)

    def getDateFromIndex(self, index:QModelIndex)->TimeSeriesDatum:
        assert isinstance(index, QModelIndex)
        if index.isValid():
            return self.items[index.row()]
        return None

    def getTimeSeriesDatumFromIndex(self, index:QModelIndex)->TimeSeriesDatum:
        assert isinstance(index, QModelIndex)
        if index.isValid():
            i = index.row()
            if i >= 0 and i < len(self.items):
                return self.items[i]

        return None

    def columnCount(self, parent = QModelIndex())->int:
        return len(self.mColumnNames)

    def data(self, index, role = Qt.DisplayRole):
        if role is None or not index.isValid():
            return None

        value = None
        columnName = self.mColumnNames[index.column()]

        TSD = self.getTimeSeriesDatumFromIndex(index)
        assert isinstance(TSD, TimeSeriesDatum)
        keys = list(TSD.__dict__.keys())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        tssList = TSD.sources()

        if role == Qt.DisplayRole or role == Qt.ToolTipRole:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if columnName == self.cnSensor:
                if role == Qt.ToolTipRole:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    value = TSD.sensor().description()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                else:
                    value = TSD.sensor().name()
            elif columnName == self.cnDate:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                value = '{}'.format(TSD.date())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            elif columnName == self.cnImages:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                value = '\n'.join(TSD.sourceUris())
            elif columnName == self.cnCRS:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                value = '\n'.join([tss.crs().description() for tss in tssList])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            elif columnName == self.cnNB:
                value = TSD.sensor().nb
            elif columnName == self.cnNL:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                value = '\n'.join([str(tss.nl) for tss in tssList])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            elif columnName == self.cnNS:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                value = '\n'.join([str(tss.ns) for tss in tssList])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            elif columnName == self.cnSensor:
                value = TSD.sensor().name()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            elif columnName in keys:
                value = TSD.__dict__[columnName]
            else:
                s = ""
        elif role == Qt.CheckStateRole:
            if columnName == self.cnDate:
                value = Qt.Checked if TSD.isVisible() else Qt.Unchecked
        elif role == Qt.BackgroundColorRole:
            value = None
        elif role == Qt.UserRole:
            value = TSD

        return value

    def setData(self, index, value, role=None):
        if role is None or not index.isValid():
            return None

        if role is Qt.UserRole:

            s = ""

        columnName = self.mColumnNames[index.column()]

        TSD = self.getTimeSeriesDatumFromIndex(index)
        if columnName == self.cnDate and role == Qt.CheckStateRole:
            TSD.setVisibility(value != Qt.Unchecked)
            return True
        else:
            return False

        return False

    def flags(self, index):
        if index.isValid():
            columnName = self.mColumnNames[index.column()]
            flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
            if columnName == self.cnDate: # allow check state
                flags = flags | Qt.ItemIsUserCheckable

            return flags
        return None

    def headerData(self, col, orientation, role):
        if Qt is None:
            return None
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.mColumnNames[col]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return col
        return None
def getSpatialPropertiesFromDataset(ds):
    assert isinstance(ds, gdal.Dataset)

    nb = ds.RasterCount
    nl = ds.RasterYSize
    ns = ds.RasterXSize
    proj = ds.GetGeoTransform()
    px_x = float(abs(proj[1]))
    px_y = float(abs(proj[5]))

    crs = QgsCoordinateReferenceSystem(ds.GetProjection())

    return nb, nl, ns, crs, px_x, px_y








Benjamin Jakimow's avatar
Benjamin Jakimow committed
def extractWavelengths(ds):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    # see http://www.harrisgeospatial.com/docs/ENVIHeaderFiles.html for supported wavelength units
    regWLkey = re.compile('.*wavelength[_ ]*$', re.I)
    regWLUkey = re.compile('.*wavelength[_ ]*units?$', re.I)
    regNumeric = re.compile(r"([-+]?\d*\.\d+|[-+]?\d+)", re.I)
    regWLU = re.compile('((micro|nano|centi)meters)|(um|nm|mm|cm|m|GHz|MHz)', re.I)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    if isinstance(ds, QgsRasterLayer):
        lyr = ds
        md = [l.split('=') for l in str(lyr.metadata()).splitlines() if 'wavelength' in l.lower()]
        #see http://www.harrisgeospatial.com/docs/ENVIHeaderFiles.html for supported wavelength units
        for kv in md:
            key, value = kv
            key = key.lower()
            if key == 'center wavelength':
                tmp = re.findall(r'\d*\.\d+|\d+', value) #find floats
                if len(tmp) == 0:
                    tmp = re.findall(r'\d+', value) #find integers
                if len(tmp) == lyr.bandCount():
                    wl = [float(w) for w in tmp]

            if key == 'wavelength units':
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    wlu = match.group()

                names = ['nanometers','micrometers','millimeters','centimeters','decimenters']
                si   = ['nm','um','mm','cm','dm']
                if wlu in names:
                    wlu = si[names.index(wlu)]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    elif isinstance(ds, gdal.Dataset):

        for domain in ds.GetMetadataDomainList():
            md = ds.GetMetadata_Dict(domain)
            for key, value in md.items():
                if wl is None and regWLkey.search(key):
                    numbers = regNumeric.findall(value)
                    if len(numbers) == ds.RasterCount:
                        wl = [float(n) for n in numbers]

                if wlu is None and regWLUkey.search(key):
                    match = regWLU.search(value)
                    if match:
                        wlu = match.group().lower()
                    names = ['nanometers', 'micrometers', 'millimeters', 'centimeters', 'decimeters']
                    si = ['nm', 'um', 'mm', 'cm', 'dm']
                    if wlu in names:
                        wlu = si[names.index(wlu)]

class TimeSeriesDockUI(QgsDockWidget, loadUI('timeseriesdock.ui')):
    """
    QgsDockWidget that shows the TimeSeries
    """
    def __init__(self, parent=None):
        super(TimeSeriesDockUI, self).__init__(parent)
        self.setupUi(self)

        #self.progressBar.setMinimum(0)
        #self.setProgressInfo(0, 100, 'Add images to fill time series')
        #self.progressBar.setValue(0)
        #self.progressInfo.setText(None)
        self.frameFilters.setVisible(False)

        self.mTimeSeries = None
        self.mSelectionModel = None


    def initActions(self, parent):

        from eotimeseriesviewer.main import TimeSeriesViewerUI
        assert isinstance(parent, TimeSeriesViewerUI)
        self.btnAddTSD.setDefaultAction(parent.actionAddTSD)
        self.btnRemoveTSD.setDefaultAction(parent.actionRemoveTSD)
        self.btnLoadTS.setDefaultAction(parent.actionLoadTS)
        self.btnSaveTS.setDefaultAction(parent.actionSaveTS)
        self.btnClearTS.setDefaultAction(parent.actionClearTS)


    def showTSD(self, tsd:TimeSeriesDatum):
        assert isinstance(self.timeSeriesTreeView, TimeSeriesTreeView)
        assert isinstance(self.mTSProxyModel, QSortFilterProxyModel)

        tsd.setVisibility(True)

        assert isinstance(self.mTimeSeries, TimeSeries)
        idxSrc = self.mTimeSeries.tsdToIdx(tsd)

        if isinstance(idxSrc, QModelIndex):
            idx2 = self.mTSProxyModel.mapFromSource(idxSrc)
            if isinstance(idx2, QModelIndex):
                self.timeSeriesTreeView.setCurrentIndex(idx2)
                self.timeSeriesTreeView.scrollTo(idx2, QAbstractItemView.PositionAtCenter)

    def updateSummary(self):


        if isinstance(self.mTimeSeries, TimeSeries):
            if len(self.mTimeSeries) == 0:
                info = 'Empty Timeseries. Please add source images.'
            else:
                nDates = self.mTimeSeries.rowCount()
                nSensors = len(self.mTimeSeries.sensors())
                nImages = len(list(self.mTimeSeries.sources()))

                info = '{} dates, {} sensors, {} source images'.format(nDates, nSensors, nImages)
        else:
            info = ''
        self.summary.setText(info)

    def onSelectionChanged(self, *args):
        """
        Slot to react on user-driven changes of the selected TimeSeriesDatum rows.
        """

        self.btnRemoveTSD.setEnabled(
            isinstance(self.mSelectionModel, QItemSelectionModel) and
            len(self.mSelectionModel.selectedRows()) > 0)

    def selectedTimeSeriesDates(self)->list:
        """
        Returns the TimeSeriesDatum selected by a user.
        :return: [list-of-TimeSeriesDatum]
        """
        if isinstance(self.mSelectionModel, QItemSelectionModel):
            return [self.mTSProxyModel.data(idx, Qt.UserRole) for idx in self.mSelectionModel.selectedRows()]
        return []

    def setTimeSeries(self, TS:TimeSeries):
        """
        Sets the TimeSeries to be shown in the TimeSeriesDockUI
        :param TS: TimeSeries
        """
        from eotimeseriesviewer.timeseries import TimeSeries
        if isinstance(TS, TimeSeries):
            self.mTimeSeries = TS
            self.mTSProxyModel = QSortFilterProxyModel(self)
            self.mTSProxyModel.setSourceModel(self.mTimeSeries)
            self.mSelectionModel = QItemSelectionModel(self.mTSProxyModel)
            self.mSelectionModel.selectionChanged.connect(self.onSelectionChanged)


            self.timeSeriesTreeView.setModel(self.mTSProxyModel)
            self.timeSeriesTreeView.setSelectionModel(self.mSelectionModel)

            for c in range(self.mTSProxyModel.columnCount()):
                self.timeSeriesTreeView.header().setSectionResizeMode(c, QHeaderView.ResizeToContents)
            self.mTimeSeries.rowsInserted.connect(self.updateSummary)
            #self.mTimeSeries.dataChanged.connect(self.updateSummary)
            self.mTimeSeries.rowsRemoved.connect(self.updateSummary)
            #TS.sigLoadingProgress.connect(self.setProgressInfo)

        self.onSelectionChanged()






Benjamin Jakimow's avatar
Benjamin Jakimow committed
if __name__ == '__main__':
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    print(convertMetricUnit(100, 'cm', 'm'))
    print(convertMetricUnit(1, 'm', 'um'))