Skip to content
Snippets Groups Projects
timeseries.py 45.1 KiB
Newer Older
        :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
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def addSources(self, sources:list):
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.sigLoadingProgress.emit(0, nMax, 'Start loading {} sources...'.format(nMax))

        # 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
            try:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    tss = source
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                assert isinstance(tss, TimeSeriesSource)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                date = tss.date()
                sid = tss.sid()
                sensor = self.sensor(sid)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                # if necessary, add a new sensor instance
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if not isinstance(sensor, SensorInstrument):
                    sensor = self.addSensor(SensorInstrument(sid))
                assert isinstance(sensor, SensorInstrument)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
                tsd = self.tsd(date, sensor)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
                # if necessary, add a new TimeSeriesDatum instance
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                if not isinstance(tsd, TimeSeriesDatum):
                    tsd = self.insertTSD(TimeSeriesDatum(self, date, sensor))
                    addedDates.append(tsd)
                assert isinstance(tsd, TimeSeriesDatum)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
                # add the source
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                tsd.addSource(tss)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            except Exception as ex:
                msg = 'Unable to add: {}\n{}'.format(str(source), str(ex))
                print(msg, file=sys.stderr)
            self.sigLoadingProgress.emit(i+1, nMax, msg)
        if len(addedDates) > 0:
            self.sigTimeSeriesDatesAdded.emit(addedDates)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    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


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)



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 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)]
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'))