Skip to content
Snippets Groups Projects
sensecarbon_tsv.py 59.4 KiB
Newer Older
unknown's avatar
unknown committed
# -*- coding: utf-8 -*-
"""
/***************************************************************************
 EnMAPBox
                                 A QGIS plugin
 EnMAP-Box V3
                              -------------------
        begin                : 2015-08-20
        git sha              : $Format:%H$
        copyright            : (C) 2015 by HU-Berlin
        email                : bj@geo.hu-berlin.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
unknown's avatar
unknown committed

# Import the code for the dialog
import os, sys, re, fnmatch, collections, copy, traceback
from qgis.core import *
#os.environ['PATH'] += os.pathsep + r'C:\OSGeo4W64\bin'

from osgeo import gdal, ogr, osr, gdal_array



unknown's avatar
unknown committed
try:
    from qgis.gui import *
unknown's avatar
unknown committed
    import qgis
unknown's avatar
unknown committed
    import qgis_add_ins
unknown's avatar
unknown committed
    qgis_available = True
except:
    qgis_available = False

import numpy as np
import pickle
unknown's avatar
unknown committed
import six
import multiprocessing
Benjamin Jakimow's avatar
Benjamin Jakimow committed
import imagechipviewsettings_widget

#I don't know why but this is required to run this in QGIS

unknown's avatar
unknown committed
path = os.path.abspath(os.path.join(sys.exec_prefix, '../../bin/pythonw.exe'))
if os.path.exists(path):
    multiprocessing.set_executable(path)
    sys.argv = [ None ]
unknown's avatar
unknown committed

pluginDir = os.path.dirname(__file__)
sys.path.append(pluginDir)
sys.path.append(os.path.join(pluginDir, 'libs'))
#sys.path.append(os.path.join(pluginDir, *['libs','qimage2ndarray']))
sys.path.append(os.path.join(pluginDir, *['libs','qrangeslider-0.1.1']))
unknown's avatar
unknown committed
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sensecarbon_tsv_gui import SenseCarbon_TSVGui

unknown's avatar
unknown committed
DEBUG = True

regLandsatSceneID = re.compile(r"L[EMCT][1234578]{1}[12]\d{12}[a-zA-Z]{3}\d{2}")

def file_search(rootdir, wildcard, recursive=False, ignoreCase=False):
    assert rootdir is not None
    if not os.path.isdir(rootdir):
        six.print_("Path is not a directory:{}".format(rootdir), file=sys.stderr)

    results = []

    for root, dirs, files in os.walk(rootdir):
        for file in files:
            if (ignoreCase and fnmatch.fnmatch(file.lower(), wildcard.lower())) \
                    or fnmatch.fnmatch(file, wildcard):
                results.append(os.path.join(root, file))
        if not recursive:
            break
    return results


class TimeSeriesTableModel(QAbstractTableModel):
    columnames = ['date','sensor','ns','nl','nb','image','mask']
unknown's avatar
unknown committed

    def __init__(self, TS, parent=None, *args):
        super(QAbstractTableModel, self).__init__()
        assert isinstance(TS, TimeSeries)
        self.TS = TS

    def rowCount(self, parent = QModelIndex()):
        return len(self.TS)

    def columnCount(self, parent = QModelIndex()):
        return len(self.columnames)

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

        self.endRemoveRows()

unknown's avatar
unknown committed
    def getDateFromIndex(self, index):
        if index.isValid():
            i = index.row()
            if i >= 0 and i < len(self.TS):
                return self.TS.getTSDs()[i]
unknown's avatar
unknown committed
        return None
unknown's avatar
unknown committed

    def getTimeSeriesDatumFromIndex(self, index):

        if index.isValid():
            i = index.row()
            if i >= 0 and i < len(self.TS):
                date = self.TS.getTSDs()[i]
unknown's avatar
unknown committed
                return self.TS.data[date]

        return None



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


        value = None
        ic_name = self.columnames[index.column()]
        TSD = self.getTimeSeriesDatumFromIndex(index)
        keys = list(TSD.__dict__.keys())
        if role == Qt.DisplayRole or role == Qt.ToolTipRole:
            if ic_name == 'name':
                value = os.path.basename(TSD.pathImg)
            elif ic_name == 'sensor':
                if role == Qt.ToolTipRole:
                    value = TSD.sensor.getDescription()
                else:
                    value = str(TSD.sensor)
unknown's avatar
unknown committed
            elif ic_name == 'date':
                value = '{}'.format(TSD.date)
            elif ic_name == 'image':
                value = TSD.pathImg
            elif ic_name == 'mask':
                value = TSD.pathMsk
            elif ic_name in keys:
                value = TSD.__dict__[ic_name]
            else:
                s = ""
        elif role == Qt.BackgroundColorRole:
            value = None
        elif role == Qt.UserRole:
unknown's avatar
unknown committed

        return value

    #def flags(self, index):
    #    return Qt.ItemIsEnabled

    def flags(self, index):
        if index.isValid():
            item = self.getTimeSeriesDatumFromIndex(index)
            cname = self.columnames[index.column()]
            if cname.startswith('d'): #relative values can be edited
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
            else:
                flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable
            return flags
            #return item.qt_flags(index.column())
        return None

    def headerData(self, col, orientation, role):
        if Qt is None:
            return None
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.columnames[col]
        elif orientation == Qt.Vertical and role == Qt.DisplayRole:
            return col
        return None

unknown's avatar
unknown committed
class TimeSeriesItemModel(QAbstractItemModel):
unknown's avatar
unknown committed

unknown's avatar
unknown committed
    def __init__(self, TS):
unknown's avatar
unknown committed
        QAbstractItemModel.__init__(self)
        #self.rootItem = TreeItem[]
unknown's avatar
unknown committed
        assert type(TS) is TimeSeries
        self.TS = TS
unknown's avatar
unknown committed

    def index(self, row, column, parent = QModelIndex()):
        if not parent.isValid():
            parentItem = self.rootItem
        else:
unknown's avatar
unknown committed
            parentItem = parent.internalPointer()
        childItem = parentItem.child(row)
        if childItem:
            return self.createIndex(row, column, childItem)
        else:
            return QModelIndex()

    def setData(self, index, value, role = Qt.EditRole):
        if role == Qt.EditRole:
            row = index.row()

            return False
        return False

    def data(self, index, role=Qt.DisplayRole):
        data = None
        if role == Qt.DisplayRole or role == Qt.EditRole:
            data = 'sampletext'


        return data

    def flags(self, QModelIndex):
        return Qt.ItemIsSelectable

    def rowCount(self, index=QModelIndex()):
unknown's avatar
unknown committed
        return len(self.TS)
unknown's avatar
unknown committed

    #---------------------------------------------------------------------------
    def columnCount(self, index=QModelIndex()):
        return 1

LUT_SensorNames = {(6,30.,30.): 'L7 ETM+' \
                  ,(7,30.,30.): 'L8 OLI' \
                  ,(4,10.,10.): 'S2 MSI 10m' \
                  ,(6,20.,20.): 'S2 MSI 20m' \
                  ,(3,30.,30.): 'S2 MSI 60m' \
                  ,(3,30.,30.): 'S2 MSI 60m' \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                  ,(5,5.,5.): 'RE 5m' \
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
class BandView(object):

Benjamin Jakimow's avatar
Benjamin Jakimow committed


    def __init__(self, TS):
        assert type(TS) is TimeSeries
        self.bandMappings = collections.OrderedDict()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.TS = TS
        self.TS.sensorAdded.connect(self.initSensor)

        self.Sensors = self.TS.Sensors
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for sensor in self.Sensors:
            self.initSensor(copy.deepcopy(sensor))
Benjamin Jakimow's avatar
Benjamin Jakimow committed





    def initSensor(self, sensor):
        assert type(sensor) is SensorConfiguration
        if sensor not in self.bandMappings.keys():
            #self.bandMappings[sensor] = ((0, 0, 5000), (1, 0, 5000), (2, 0, 5000))
            x = imagechipviewsettings_widget.ImageChipViewSettings(sensor)
            x.create()
            self.bandMappings[sensor] = x
Benjamin Jakimow's avatar
Benjamin Jakimow committed


    def getSensorStats(self, sensor, bands):
        assert type(sensor) is SensorConfiguration
        dsRef = gdal.Open(self.Sensors[sensor][0])
        return [dsRef.GetRasterBand(b).ComputeRasterMinMax() for b in bands]


    def getRanges(self, sensor):
        return self.getWidget(sensor).getRanges()

    def getWidget(self, sensor):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert type(sensor) is SensorConfiguration
        return self.bandMappings[sensor]

    def getBands(self, sensor):
        return self.getWidget(sensor).getBands()


    def useMaskValues(self):

        #todo:
        return False

Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
class SensorConfiguration(object):
    """
    Describes a Sensor Configuration
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __init__(self,nb, px_size_x,px_size_y, band_names=None, wavelengths=None, sensor_name=None):

        assert nb >= 1

        self.TS = None
        self.nb = nb
        self.px_size_x = float(abs(px_size_x))
        self.px_size_y = float(abs(px_size_y))

        assert self.px_size_x > 0
        assert self.px_size_y > 0
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if band_names is not None:
            assert len(band_names) == nb
        else:
            band_names = ['Band {}'.format(b+1) for b in range(nb)]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.band_names = band_names

        if wavelengths is not None:
            assert len(wavelengths) == nb
        self.wavelengths = wavelengths

        if sensor_name is None:
            id = (self.nb, self.px_size_x, self.px_size_y)
            if id in LUT_SensorNames.keys():
                sensor_name = LUT_SensorNames[id]
            else:
                sensor_name = '{} b x {} m'.format(self.nb, self.px_size_x)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.sensor_name = sensor_name

        self.hashvalue = hash(','.join(self.band_names))

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __eq__(self, other):
        return self.nb == other.nb and self.px_size_x == other.px_size_x and self.px_size_y == other.px_size_y

    def __hash__(self):
        return self.hashvalue
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __repr__(self):
        return self.sensor_name
    def getDescription(self):
        info = []
        info.append(self.sensor_name)
        info.append('{} Bands'.format(self.nb))
        info.append('Band\tName\tWavelength')
        for b in range(self.nb):
            if self.wavelengths:
                wl = str(self.wavelengths[b])
            else:
                wl = 'unknown'
            info.append('{}\t{}\t{}'.format(b+1, self.band_names[b],wl))
unknown's avatar
unknown committed

class TimeSeries(QObject):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    datumAdded = pyqtSignal(name='datumAdded')

    #fire when a new sensor configuration is added
    sensorAdded = pyqtSignal(object, name='sensorAdded')


unknown's avatar
unknown committed
    changed = pyqtSignal()
unknown's avatar
unknown committed
    progress = pyqtSignal(int,int,int, name='progress')
    chipLoaded = pyqtSignal(object, name='chiploaded')
    closed = pyqtSignal()
    error = pyqtSignal(object)

    def __init__(self, imageFiles=None, maskFiles=None):
        QObject.__init__(self)
unknown's avatar
unknown committed

unknown's avatar
unknown committed

        #fire when a new TSD is added
unknown's avatar
unknown committed

        self.data = collections.OrderedDict()

        self.CHIP_BUFFER=dict()

        self.shape = None

        self.Sensors = collections.OrderedDict()
unknown's avatar
unknown committed

        self.Pool = None

        if imageFiles is not None:
            self.addFiles(imageFiles)
        if maskFiles is not None:
            self.addMasks(maskFiles)

    _sep = ';'

    @staticmethod
    def loadFromFile(path):

        images = []
        masks = []
        with open(path, 'r') as f:
            lines = f.readlines()
            for l in lines:
                if re.match('^[ ]*[;#&]', l):
                    continue

                parts = re.split('[\n'+TimeSeries._sep+']', l)
                parts = [p for p in parts if p != '']
                images.append(parts[0])
                if len(parts) > 1:
                    masks.append(parts[1])

        TS = TimeSeries()
        TS.addFiles(images)
        TS.addMasks(masks)

        return TS


    def saveToFile(self, path):
        import time
        lines = []
        lines.append('#Time series definition file: {}'.format(np.datetime64('now').astype(str)))
        lines.append('#<image path>[;<mask path>]')
        for TSD in self.data.values():

            line = TSD.pathImg
            if TSD.pathMsk is not None:
                line += TimeSeries._sep + TSD.pathMsk

            lines.append(line)

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

        print('Write {}'.format(path))
        with open(path, 'w') as f:
            f.writelines(lines)


Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getSRS(self):
        if len(self.data) == 0:
            return 0
        else:
            TSD = self.data[self.getTSDs()[0]]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            return TSD.getSpatialReference()
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getWKT(self):
        srs = self.getSRS()
        return srs.ExportToWkt()
unknown's avatar
unknown committed

    def getSceneCenter(self, srs=None):

        if srs is None:
            srs = self.getSRS()

        bbs = list()
        for S, TSDs in self.Sensors.items():
            x = []
            y = []
            for TSD in TSDs:
                bb = TSD.getBoundingBox(srs)
                x.extend([c[0] for c in bb])
                y.extend([c[1] for c in bb])

        return None
        pass

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getMaxExtent(self, srs=None):
unknown's avatar
unknown committed

        x = []
        y = []

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if srs is None:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            srs = self.getSRS()
unknown's avatar
unknown committed
        for TSD in self.data.values():
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            bb = TSD.getBoundingBox(srs)
unknown's avatar
unknown committed
            x.extend([c[0] for c in bb])
            y.extend([c[1] for c in bb])

        return (min(x), min(y), max(x), max(y))

    def getObservationDates(self):
        return [tsd.getDate() for tsd in self.data.keys()]

    def getTSDs(self, date_of_interest=None):
        if date_of_interest:
            tsds = [tsd for tsd in self.data.keys() if tsd.getDate() == date_of_interest]
        else:
            tsds = list(self.data.keys())
        return tsds
unknown's avatar
unknown committed

    def _callback_error(self, error):
        six.print_(error, file=sys.stderr)
        self.error.emit(error)
        self._callback_progress()

    def _callback_spatialchips(self, results):
        self.chipLoaded.emit(results)
        self._callback_progress()

    def _callback_progress(self):
        self._callback_progress_done += 1
        self.progress.emit(0, self._callback_progress_done, self._callback_progress_max)

        if self._callback_progress_done >= self._callback_progress_max:
            self._callback_progress_done = 0
            self._callback_progress_max = 0
            self.progress.emit(0,0,1)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getSpatialChips_parallel(self, bbWkt, srsWkt, TSD_band_list, ncpu=1):
unknown's avatar
unknown committed
        assert type(bbWkt) is str and type(srsWkt) is str

        import multiprocessing
        if self.Pool is not None:
            self.Pool.terminate()

        self.Pool = multiprocessing.Pool(processes=ncpu)


Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self._callback_progress_max = len(TSD_band_list)
unknown's avatar
unknown committed
        self._callback_progress_done = 0

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for T in TSD_band_list:
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
            TSD = copy.deepcopy(T[0])
            bands = T[1]
unknown's avatar
unknown committed
            #TSD = pickle.dumps(self.data[date])
            args = (TSD, bbWkt, srsWkt)
            kwds = {'bands':bands}

unknown's avatar
unknown committed
            if six.PY3:
                self.Pool.apply_async(PFunc_TimeSeries_getSpatialChip, \
                                 args=args, kwds=kwds, \
                                 callback=self._callback_spatialchips, error_callback=self._callback_error)
            else:
                self.Pool.apply_async(PFunc_TimeSeries_getSpatialChip, \
                                      args, kwds, self._callback_spatialchips)
unknown's avatar
unknown committed

        s = ""

        pass

    def getImageChips(self, xy, size=50, bands=[4,5,6], dates=None):
        chipCollection = collections.OrderedDict()

        if dates is None:
            dates = self.data.keys()
        for date in dates:
            TSD = self.data[date]
            chipCollection[date] = TSD.readImageChip(xy, size=size, bands=bands)

        return chipCollection

    def addMasks(self, files, raise_errors=True, mask_value=0, exclude_mask_value=True):
        assert isinstance(files, list)
        l = len(files)

        self.progress.emit(0,0,l)
        for i, file in enumerate(files):
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            try:
                self.addMask(file, raise_errors=raise_errors, mask_value=mask_value, exclude_mask_value=exclude_mask_value, _quiet=True)
            except:
                pass

unknown's avatar
unknown committed
            self.progress.emit(0,i+1,l)
unknown's avatar
unknown committed

unknown's avatar
unknown committed
        self.progress.emit(0,0,l)
unknown's avatar
unknown committed

unknown's avatar
unknown committed
    def addMask(self, pathMsk, raise_errors=True, mask_value=0, exclude_mask_value=True, _quiet=False):
unknown's avatar
unknown committed
        print('Add mask {}...'.format(pathMsk))
        ds = getDS(pathMsk)
        date = getImageDate(ds)

        if date in self.data.keys():
            TSD = self.data[date]
unknown's avatar
unknown committed

            if not _quiet:
                self.changed.emit()

unknown's avatar
unknown committed
            return TSD.setMask(pathMsk, raise_errors=raise_errors, mask_value=mask_value, exclude_mask_value=exclude_mask_value)
        else:
            info = 'TimeSeries does not contain date {} {}'.format(date, pathMsk)
            if raise_errors:
                raise Exception(info)
            else:
                six.print_(info, file=sys.stderr)
            return False

unknown's avatar
unknown committed
    def removeAll(self):
        self.clear()

    def clear(self):
        self.Sensors.clear()
        self.data.clear()
unknown's avatar
unknown committed
        self.changed.emit()


    def removeDates(self, TSDs):
        for TSD in TSDs:
            self.removeTSD(TSD, _quiet=True)
unknown's avatar
unknown committed
        self.changed.emit()

    def removeTSD(self, TSD, _quiet=False):
        assert type(TSD) is TimeSeriesDatum
        S = TSD.sensor
        self.Sensors[S].remove(TSD)
        self.data.pop(TSD, None)
unknown's avatar
unknown committed
        if not _quiet:
            self.changed.emit()
unknown's avatar
unknown committed

unknown's avatar
unknown committed

    def addFile(self, pathImg, pathMsk=None, _quiet=False):
unknown's avatar
unknown committed
        print(pathImg)
unknown's avatar
unknown committed
        print('Add image {}...'.format(pathImg))

        try:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            sensorAdded = False
            TSD = TimeSeriesDatum(pathImg, pathMsk=pathMsk)
            existingSensors = list(self.Sensors.keys())
            if TSD.sensor not in existingSensors:
                self.Sensors[TSD.sensor] = list()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                sensorAdded = True
            else:
                TSD.sensor = existingSensors[existingSensors.index(TSD.sensor)]
unknown's avatar
unknown committed

            if TSD in self.data.keys():
                six.print_('Time series datum already added: {}'.format(str(TSD)), file=sys.stderr)
            else:
                self.Sensors[TSD.sensor].append(TSD)
                self.data[TSD] = TSD
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            if sensorAdded:
                self.sensorAdded.emit(TSD.sensor)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

            if not _quiet:
                self._sortTimeSeriesData()
                self.changed.emit()
                self.datumAdded.emit()
        except:
            exc_type, exc_value, exc_traceback = sys.exc_info()
            traceback.print_exception(exc_type, exc_value, exc_traceback, limit=2)
            six.print_('Unable to add {}'.format(file), file=sys.stderr)
            pass
unknown's avatar
unknown committed


unknown's avatar
unknown committed

unknown's avatar
unknown committed
    def addFiles(self, files):
        assert isinstance(files, list)
        l = len(files)
        assert l > 0

        self.progress.emit(0,0,l)
        for i, file in enumerate(files):
            self.addFile(file, _quiet=True)
unknown's avatar
unknown committed
            self.progress.emit(0,i+1,l)

        self._sortTimeSeriesData()
        self.progress.emit(0,0,l)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.datumAdded.emit()
unknown's avatar
unknown committed



    def _sortTimeSeriesData(self):
        self.data = collections.OrderedDict(sorted(self.data.items(), key=lambda t:t[0]))

    def __len__(self):
        return len(self.data)

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

        if l > 0:
            keys = list(self.data.keys())
            info.append('  Range: {} to {}'.format(keys[0], keys[-1]))
        return '\n'.join(info)

def PFunc_TimeSeries_getSpatialChip(TSD, bbWkt, srsWkt , bands=[4,5,3]):

    chipdata = TSD.readSpatialChip(bbWkt, srs=srsWkt, bands=bands)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    return TSD, chipdata
unknown's avatar
unknown committed

def px2Coordinate(gt, pxX, pxY, upper_left=True):
    cx = gt[0] + pxX*gt[1] + pxY*gt[2]
    cy = gt[3] + pxX*gt[4] + pxY*gt[5]
    if not upper_left:
        cx += gt[1]*0.5
        cy += gt[5]*0.5
    return cx, cy

def coordinate2px(gt, cx, cy):
    px = int((cx - gt[0]) / gt[1])
    py = int((cy - gt[3]) / gt[5])
    return px, py


def getBoundingBoxPolygon(points, srs=None):
    ring = ogr.Geometry(ogr.wkbLinearRing)
    for point in points:
        ring.AddPoint(point[0], point[1])
    bb = ogr.Geometry(ogr.wkbPolygon)
    bb.AddGeometry(ring)
    if srs:
        bb.AssignSpatialReference(srs)
    return bb



unknown's avatar
unknown committed
def getDS(ds):
    if type(ds) is not gdal.Dataset:
        ds = gdal.Open(ds)
    return ds

def getImageDate(ds):
    if type(ds) is str:
        ds = gdal.Open(ds)

    path = ds.GetFileList()[0]
    to_check = [os.path.basename(path), os.path.dirname(path)]

    regAcqDate = re.compile(r'acquisition (time|date|day)', re.I)
    for key, value in ds.GetMetadata_Dict().items():
        if regAcqDate.search(key):
            to_check.insert(0, value)

    for text in to_check:
        date = parseAcquisitionDate(text)
        if date:
            return date

    raise Exception('Can not identify acquisition date of {}'.format(path))


class TimeSeriesDatum(object):

    def __init__(self, pathImg, pathMsk=None):
        self.pathImg = pathImg
        self.pathMsk = None

        dsImg = gdal.Open(pathImg)
        assert dsImg

        date = getImageDate(dsImg)
        assert date is not None
        self.date = date.astype(str)

        self.ns = dsImg.RasterXSize
        self.nl = dsImg.RasterYSize
        self.nb = dsImg.RasterCount

        self.srs_wkt = dsImg.GetProjection()
        self.gt = list(dsImg.GetGeoTransform())

        refBand = dsImg.GetRasterBand(1)
        self.etype = refBand.DataType

        self.nodata = refBand.GetNoDataValue()

        self.bandnames = list()
unknown's avatar
unknown committed
        for b in range(self.nb):
            name = dsImg.GetRasterBand(b+1).GetDescription()
            if name is None or name == '':
                name = 'Band {}'.format(b+1)
            self.bandnames.append(name)

        self.wavelength = None
        domains = dsImg.GetMetadataDomainList()
        for domain in domains:
            md = dsImg.GetMetadata_Dict(domain)
            if 'wavelength' in md.keys():
                wl = md['wavelength']
                wl = re.split('[;,{}]', wl)
                wl = [float(w) for w in wl]
                assert len(wl) == self.nb
                self.wavelength = wl
                break
unknown's avatar
unknown committed

        self.sensor = SensorConfiguration(self.nb, self.gt[1], self.gt[5], self.bandnames, self.wavelength)


unknown's avatar
unknown committed
        if pathMsk:
            self.setMask(pathMsk)

    def getdtype(self):
        return gdal_array.GDALTypeCodeToNumericTypeCode(self.etype)

    def getDate(self):
        return np.datetime64(self.date)

    def getSpatialReference(self):
        srs = osr.SpatialReference()
        srs.ImportFromWkt(self.srs_wkt)
        return srs

    def getBoundingBox(self, srs=None):
        ext = list()


        for px in [0,self.ns]:
            for py in [0, self.nl]:
                ext.append(px2Coordinate(self.gt, px, py))



        if srs is not None:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            assert type(srs) is osr.SpatialReference
unknown's avatar
unknown committed
            my_srs = self.getSpatialReference()
            if not my_srs.IsSame(srs):
                #todo: consider srs differences
                trans = osr.CoordinateTransformation(my_srs, srs)
                ext = trans.TransformPoints(ext)
                ext = [(e[0], e[1]) for e in ext]
unknown's avatar
unknown committed

        return ext


    def setMask(self, pathMsk, raise_errors=True, mask_value=0, exclude_mask_value=True):
        dsMsk = gdal.Open(pathMsk)
        mskDate = getImageDate(dsMsk)


        errors = list()
        if mskDate and mskDate != self.getDate():
            errors.append('Mask date differs from image date')
        if self.ns != dsMsk.RasterXSize or self.nl != dsMsk.RasterYSize:
            errors.append('Spatial size differs')
        if dsMsk.RasterCount != 1:
            errors.append('Mask has > 1 bands')

        projImg = self.getSpatialReference()
        projMsk = osr.SpatialReference()
        projMsk.ImportFromWkt(dsMsk.GetProjection())

        if not projImg.IsSame(projMsk):
            errors.append('Spatial Reference differs from image')
        if self.gt != list(dsMsk.GetGeoTransform()):
            errors.append('Geotransformation differs from image')

        if len(errors) > 0:
            errors.insert(0, 'pathImg:{} \npathMsk:{}'.format(self.pathImg, pathMsk))
            errors = '\n'.join(errors)
            if raise_errors:
                raise Exception(errors)
            else:
                six.print_(errors, file=sys.stderr)
                return False
        else:
            self.pathMsk = pathMsk
            self.mask_value = mask_value
            self.exclude_mask_value = exclude_mask_value

            return True

    def readSpatialChip(self, geometry, srs=None, bands=[4,5,3]):

        srs_img = osr.SpatialReference()
        srs_img.ImportFromWkt(self.srs_wkt)


        if type(geometry) is ogr.Geometry:
            g_bb = geometry
            srs_bb = g_bb.GetSpatialReference()
unknown's avatar
unknown committed
        else:
            assert srs is not None and type(srs) in [str, osr.SpatialReference]
            if type(srs) is str:
                srs_bb = osr.SpatialReference()
                srs_bb.ImportFromWkt(srs)
            else:
                srs_bb = srs.Clone()
            g_bb = ogr.CreateGeometryFromWkt(geometry, srs_bb)
unknown's avatar
unknown committed

        assert srs_bb is not None and g_bb is not None
        assert g_bb.GetGeometryName() == 'POLYGON'
unknown's avatar
unknown committed

        if not srs_img.IsSame(srs_bb):
            g_bb = g_bb.Clone()
            g_bb.TransformTo(srs_img)

        cx0,cx1,cy0,cy1 = g_bb.GetEnvelope()
unknown's avatar
unknown committed

        ul_px = coordinate2px(self.gt, min([cx0, cx1]), max([cy0, cy1]))
        lr_px = coordinate2px(self.gt, max([cx0, cx1]), min([cy0, cy1]))
        lr_px = [c+1 for c in lr_px] #+1

        return self.readImageChip([ul_px[0], lr_px[0]], [ul_px[1], lr_px[1]], bands=bands)

    def readImageChip(self, px_x, px_y, bands=[4,5,3]):

        ds = gdal.Open(self.pathImg, gdal.GA_ReadOnly)
unknown's avatar
unknown committed

        assert len(px_x) == 2 and px_x[0] <= px_x[1]
        assert len(px_y) == 2 and px_y[0] <= px_y[1]

        ns = px_x[1]-px_x[0]+1
        nl = px_y[1]-px_y[0]+1
        assert ns >= 0
        assert nl >= 0
unknown's avatar
unknown committed

        src_ns = ds.RasterXSize
        src_nl = ds.RasterYSize


        chipdata = dict()



        #pixel indices in source image
        x0 = max([0, px_x[0]])
        y0 = max([0, px_y[0]])
        x1 = min([src_ns, px_x[1]])
        y1 = min([src_nl, px_y[1]])
        win_xsize = x1-x0+1
        win_ysize = y1-y0+1

        #pixel indices in image chip (ideally 0 and ns-1 or nl-1)
        i0 = x0 - px_x[0]
        i1 = i0 + win_xsize

        j0 = y0 - px_y[0]
        j1 = j0+ win_ysize




        templateImg = np.zeros((nl,ns))
        if self.nodata:
            templateImg *= self.nodata

unknown's avatar
unknown committed
        templateImg = templateImg.astype(self.getdtype())
        templateMsk = np.ones((nl,ns), dtype='bool')

        if win_xsize < 1 or win_ysize < 1:
            six.print_('Selected image chip is out of raster image {}'.format(self.pathImg), file=sys.stderr)
unknown's avatar
unknown committed
            for i, b in enumerate(bands):
                chipdata[b] = np.copy(templateImg)

        else:
            for i, b in enumerate(bands):
                band = ds.GetRasterBand(b)
                data = np.copy(templateImg)
                data[j0:j1,i0:i1] = band.ReadAsArray(xoff=x0, yoff=y0, win_xsize=win_xsize,win_ysize=win_ysize)
                chipdata[b] = data
                nodatavalue = band.GetNoDataValue()
                if nodatavalue is not None:
                    templateMsk[j0:j1,i0:i1] = np.logical_and(templateMsk[j0:j1,i0:i1], data[j0:j1,i0:i1] != nodatavalue)

            if self.pathMsk:
                ds = gdal.Open(self.pathMsk)
                tmp = ds.GetRasterBand(1).ReadAsArray(xoff=x0, yoff=y0, \
                            win_xsize=win_xsize,win_ysize=win_ysize) == 0

                templateMsk[j0:j1,i0:i1] = np.logical_and(templateMsk[j0:j1,i0:i1], tmp)

        chipdata['mask'] = templateMsk
        return chipdata

    def __repr__(self):
        return 'TS Datum {} {}'.format(self.date, str(self.sensor))

    def __cmp__(self, other):
        return cmp(str((self.date, self.sensor)), str((other.date, other.sensor)))

    def __eq__(self, other):
        return self.date == other.date and self.sensor == other.sensor

    def __hash__(self):
        return hash((self.date,self.sensor.sensor_name))

unknown's avatar
unknown committed

regYYYYDOY = re.compile(r'(19|20)\d{5}')
regYYYYMMDD = re.compile(r'(19|20)\d{2}-\d{2}-\d{2}')
unknown's avatar
unknown committed
regYYYY = re.compile(r'(19|20)\d{2}')
unknown's avatar
unknown committed
def parseAcquisitionDate(text):
    match = regYYYYMMDD.search(text)
    if match:
        return np.datetime64(match.group())
unknown's avatar
unknown committed
    match = regYYYYDOY.search(text)
unknown's avatar
unknown committed
    if match:
        return getDateTime64FromYYYYDOY(match.group())
unknown's avatar
unknown committed
    match = regYYYY.search(text)
    if match:
        return np.datetime64(match.group())
unknown's avatar
unknown committed
    return None


unknown's avatar
unknown committed

unknown's avatar
unknown committed
def getDateTime64FromYYYYDOY(yyyydoy):
    return getDateTime64FromDOY(yyyydoy[0:4], yyyydoy[4:7])

def getDateTime64FromDOY(year, doy):
        if type(year) is str:
            year = int(year)
        if type(doy) is str:
            doy = int(doy)
        return np.datetime64('{:04d}-01-01'.format(year)) + np.timedelta64(doy-1, 'D')


class PictureTest(QMainWindow):

    def __init__(self, parent=None, qImage=None):
unknown's avatar
unknown committed
        super(PictureTest,self).__init__(parent)
        self.setWindowTitle("Show Image with pyqt")
        self.imageLabel=QLabel()
        self.imageLabel.setSizePolicy(QSizePolicy.Ignored,QSizePolicy.Ignored)
        self.setCentralWidget(self.imageLabel)

        self.cv_img = None

        if qImage:
            self.addImage(qImage)
unknown's avatar
unknown committed

    def addImage(self, qImage):
        pxmap = QPixmap.fromImage(qImage)
        self.addPixmap(pxmap)

    def addPixmap(self, pixmap):
        pxmap = pixmap.scaled(self.imageLabel.size(), Qt.KeepAspectRatio)
        self.imageLabel.setPixmap(pxmap)
        self.imageLabel.adjustSize()
        self.imageLabel.update()

    def addNumpy(self, data):
unknown's avatar
unknown committed


        img = Array2Image(data)
unknown's avatar
unknown committed

        #self.resize(img.width(), img.height())

def getChip3d(chips, rgb_idx, ranges):
    assert len(rgb_idx) == 3 and len(rgb_idx) == len(ranges)
    for i in rgb_idx:
        assert i in chips.keys()

    nl, ns = chips[rgb_idx[0]].shape
    a3d = np.ndarray((3,nl,ns), dtype='float')

    for i, rgb_i in enumerate(rgb_idx):
        range = ranges[i]
        data = chips[rgb_i].astype('float')
        data -= range[0]
        data *= 255./range[1]
        a3d[i,:] = data

    np.clip(a3d, 0, 255, out=a3d)

    return a3d.astype('uint8')

def Array2Image(d3d):
    nb, nl, ns = d3d.shape
    byteperline = nb
    d3d = d3d.transpose([1,2,0]).copy()

    return QImage(d3d.data, ns, nl, QImage.Format_RGB888)

class VerticalLabel(QLabel):
    def __init__(self, text):
        super(self.__class__, self).__init__()
        self.text = text

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.setPen(Qt.black)
        painter.translate(20, 100)
        painter.rotate(-90)
        if self.text:
            painter.drawText(0, 0, self.text)
        painter.end()
unknown's avatar
unknown committed

    def minimumSizeHint(self):
        size = QLabel.minimumSizeHint(self)
        return QSize(size.height(), size.width())

    def sizeHint(self):
        size = QLabel.sizeHint(self)
        return QSize(size.height(), size.width())
unknown's avatar
unknown committed

unknown's avatar
unknown committed
class ImageChipBuffer(object):


    def __init__(self):
        self.data = dict()
        self.BBox = None
        self.SRS = None
unknown's avatar
unknown committed
        pass

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def hasDataCube(self, TSD):
        return TSD in self.data.keys()
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getMissingBands(self, TSD, bands):
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        missing = set(bands)
        if TSD in self.data.keys():
            missing = missing - set(self.data[TSD].keys())
unknown's avatar
unknown committed
        return missing

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def addDataCube(self, TSD, chipData):
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert self.BBox is not None, 'Please initialize the bounding box first.'
unknown's avatar
unknown committed
        assert isinstance(chipData, dict)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if TSD not in self.data.keys():
            self.data[TSD] = dict()
        self.data[TSD].update(chipData)

    def getDataCube(self, TSD):
        return self.data.get(TSD)
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def getChipRGB(self, TSD, band_view):
        bands = band_view.getBands(TSD.sensor)
        band_ranges = band_view.getRanges(TSD.sensor)
unknown's avatar
unknown committed
        assert len(bands) == 3 and len(bands) == len(band_ranges)
        assert TSD in self.data.keys(), 'Time Series Datum {} is not in buffer'.format(TSD.getDate())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        chipData = self.data[TSD]
unknown's avatar
unknown committed
        for b in bands:
            assert b in chipData.keys()

        nl, ns = chipData[bands[0]].shape
        rgb_data = np.ndarray((3,nl,ns), dtype='float')

        for i, b in enumerate(bands):
            range = band_ranges[i]
            data = chipData[b].astype('float')
            data -= range[0]
            data *= 255./range[1]
            rgb_data[i,:] = data

        np.clip(rgb_data, 0, 255, out=rgb_data)
        rgb_data = rgb_data.astype('uint8')

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if band_view.useMaskValues():
            rgb = band_view.getMaskColor()
unknown's avatar
unknown committed
            is_masked = np.where(np.logical_not(chipData['mask']))
            for i, c in enumerate(rgb):
                rgb_data[i, is_masked[0], is_masked[1]] = c

unknown's avatar
unknown committed

    def getChipImage(self, date, view):
        rgb = self.getChipRGB(date, view)
        nb, nl, ns = rgb.shape
        rgb = rgb.transpose([1,2,0]).copy('C')
        return QImage(rgb.data, ns, nl, QImage.Format_RGB888)
unknown's avatar
unknown committed

    def clear(self):
        self.data.clear()

    def setBoundingBox(self, BBox):
        assert type(BBox) is ogr.Geometry
        SRS = BBox.GetSpatialReference()
        assert SRS is not None
        if self.BBox is None or not self.BBox.Equals(BBox) or not self.SRS.IsSame(SRS):
            self.data.clear()
            self.BBox = BBox
            self.SRS = SRS

    def __repr__(self):
        info = ['Chipbuffer']
        info.append('Bounding Box: {}'.format(self.bbBoxWkt))
        info.append('Chips: {}'.format(len(self.data)))
        return '\n'.join(info)


class SenseCarbon_TSV:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface

        #if isinstance(iface, QgsApplication):
        #self.iface = iface

unknown's avatar
unknown committed
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = 'placeholder'
        #locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'EnMAPBox_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = SenseCarbon_TSVGui()
        D = self.dlg
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        #init on empty time series
        self.TS = None
        self.init_TimeSeries()
        self.BAND_VIEWS = list()
unknown's avatar
unknown committed
        self.ImageChipBuffer = ImageChipBuffer()
        self.CHIPWIDGETS = collections.OrderedDict()

        self.ValidatorPxX = QIntValidator(0,99999)
        self.ValidatorPxY = QIntValidator(0,99999)
        D.btn_showPxCoordinate.clicked.connect(lambda: self.ua_showPxCoordinate_start())
        D.btn_selectByCoordinate.clicked.connect(self.ua_selectByCoordinate)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        D.btn_selectByRectangle.clicked.connect(self.ua_selectByRectangle)
        D.btn_addBandView.clicked.connect(lambda :self.ua_addBandView())
unknown's avatar
unknown committed
        D.btn_addTSImages.clicked.connect(lambda :self.ua_addTSImages())
        D.btn_addTSMasks.clicked.connect(lambda :self.ua_addTSMasks())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        D.btn_removeTSD.clicked.connect(lambda : self.ua_removeTSD(None))
unknown's avatar
unknown committed
        D.btn_removeTS.clicked.connect(self.ua_removeTS)
unknown's avatar
unknown committed

        D.btn_loadTSFile.clicked.connect(self.ua_loadTSFile)
        D.btn_saveTSFile.clicked.connect(self.ua_saveTSFile)
        D.btn_addTSExample.clicked.connect(self.ua_loadExampleTS)
unknown's avatar
unknown committed
        D.spinBox_ncpu.setRange(0, multiprocessing.cpu_count())

        # Declare instance attributes
        self.actions = []
        #self.menu = self.tr(u'&EnMAP-Box')
        # TODO: We are going to let the user set this up in a future iteration
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.RectangleMapTool = None
        self.PointMapTool = None
        self.canvas_srs = osr.SpatialReference()

unknown's avatar
unknown committed
        if self.iface:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.canvas = self.iface.mapCanvas()
unknown's avatar
unknown committed
            self.menu = self.tr(u'&SenseCarbon TSV')
            self.toolbar = self.iface.addToolBar(u'SenseCarbon TSV')
            self.toolbar.setObjectName(u'SenseCarbon TSV')
unknown's avatar
unknown committed

unknown's avatar
unknown committed
            self.RectangleMapTool = qgis_add_ins.RectangleMapTool(self.canvas)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.RectangleMapTool.rectangleDrawed.connect(self.ua_selectBy_Response)
unknown's avatar
unknown committed
            self.PointMapTool = qgis_add_ins.PointMapTool(self.canvas)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.PointMapTool.coordinateSelected.connect(self.ua_selectBy_Response)
            #self.RectangleMapTool.connect(self.ua_selectByRectangle_Done)
        self.ICP = self.dlg.scrollArea_imageChip_content.layout()
        self.dlg.scrollArea_bandViews_content.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
        self.BVP = self.dlg.scrollArea_bandViews_content.layout()
unknown's avatar
unknown committed
        self.check_enabled()
        s = ""

    def init_TimeSeries(self, TS=None):
        if TS is None:
            TS = TimeSeries()
        assert type(TS) is TimeSeries

        if self.TS is not None:
            disconnect_signal(self.TS.datumAdded)
            disconnect_signal(self.TS.progress)
            disconnect_signal(self.TS.chipLoaded)

        self.TS = TS
        self.TS.datumAdded.connect(self.ua_datumAdded)
        self.TS.progress.connect(self.ua_TSprogress)
        self.TS.chipLoaded.connect(self.ua_showPxCoordinate_addChips)

        TSM = TimeSeriesTableModel(self.TS)
        D = self.dlg
        D.tableView_TimeSeries.setModel(TSM)
        D.tableView_TimeSeries.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
        D.cb_centerdate.setModel(TSM)
        D.cb_centerdate.setModelColumn(0)
        D.cb_centerdate.currentIndexChanged.connect(self.scrollToDate)


    def ua_loadTSFile(self, path=None):
        if path is None or path is False:
            path = QFileDialog.getOpenFileName(self.dlg, 'Open Time Series file')

        if os.path.exists(path):
            self.ua_removeTS()
            TS = TimeSeries.loadFromFile(path)
            self.init_TimeSeries(TS)
            self.ua_datumAdded()

            if len(self.BAND_VIEWS) == 0:
                self.ua_addBandView([3, 2, 1])
                self.ua_addBandView([4, 5, 3])

        self.check_enabled()

    def ua_saveTSFile(self):
        path = QFileDialog.getSaveFileName(self.dlg, caption='Save Time Series file')
        if path is not None:
            self.TS.saveToFile(path)


    def ua_loadExampleTS(self):
        import sensecarbon_tsv
        path_example = file_search(os.path.dirname(sensecarbon_tsv.__file__), 'testdata.txt', recursive=True)
        if path_example is None or len(path_example) == 0:
            QMessageBox.information(self.dlg, 'File not found', 'testdata.txt - this file describes an exemplary time series.')
        else:
            self.ua_loadTSFile(path=path_example[0])


unknown's avatar
unknown committed
    def ua_selectByRectangle(self):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if self.RectangleMapTool is not None:
            self.canvas.setMapTool(self.RectangleMapTool)
unknown's avatar
unknown committed

    def ua_selectByCoordinate(self):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if self.PointMapTool is not None:
            self.canvas.setMapTool(self.PointMapTool)

    def setCanvasSRS(self,srs):
        if type(srs) is osr.SpatialReference:
            self.canvas_srs = srs
        else:
            self.canvas_srs.ImportFromWkt(srs)

        self.dlg.tb_srs_info.setPlainText(self.canvas_srs.ExportToProj4())

    def ua_selectBy_Response(self, geometry, srs_wkt):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        D = self.dlg
        x = D.spinBox_coordinate_x.value()
        y = D.spinBox_coordinate_x.value()
        dx = D.doubleSpinBox_subset_size_x.value()
        dy = D.doubleSpinBox_subset_size_y.value()

        self.setCanvasSRS(osr.GetUserInputAsWKT(str(srs_wkt)))

Benjamin Jakimow's avatar
Benjamin Jakimow committed

        if type(geometry) is QgsRectangle:
            center = geometry.center()
            x = center.x()
            y = center.y()

            dx = geometry.xMaximum() - geometry.xMinimum()
            dy = geometry.yMaximum() - geometry.yMinimum()

        if type(geometry) is QgsPoint:
            x = geometry.x()
            y = geometry.y()

        """
        ref_srs = self.TS.getSRS()
        if ref_srs is not None and not ref_srs.IsSame(canvas_srs):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            print('Convert canvas coordinates to time series SRS')
            g = ogr.Geometry(ogr.wkbPoint)
            g.AddPoint(x,y)
            g.AssignSpatialReference(canvas_srs)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            x = g.GetX()
            y = g.GetY()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        D.doubleSpinBox_subset_size_x.setValue(dx)
        D.doubleSpinBox_subset_size_y.setValue(dy)
        D.spinBox_coordinate_x.setValue(x)
        D.spinBox_coordinate_y.setValue(y)

    def qgs_handleMouseDown(self, pt, btn):
unknown's avatar
unknown committed


    def ua_TSprogress(self, v_min, v, v_max):
        assert v_min <= v and v <= v_max
        P = self.dlg.progressBar
        if P.minimum() != v_min or P.maximum() != v_max:
            P.setRange(v_min, v_max)
        P.setValue(v)

    def ua_datumAdded(self):
unknown's avatar
unknown committed
        if len(self.TS) > 0:
            self.setCanvasSRS(self.TS.getSRS())
unknown's avatar
unknown committed
            if self.dlg.spinBox_coordinate_x.value() == 0.0 and \
               self.dlg.spinBox_coordinate_y.value() == 0.0:
                xmin, ymin, xmax, ymax = self.TS.getMaxExtent(srs=self.canvas_srs)
unknown's avatar
unknown committed
                self.dlg.spinBox_coordinate_x.setRange(xmin, xmax)
                self.dlg.spinBox_coordinate_y.setRange(ymin, ymax)
                #x, y = self.TS.getSceneCenter()
unknown's avatar
unknown committed
                self.dlg.spinBox_coordinate_x.setValue(0.5*(xmin+xmax))
                self.dlg.spinBox_coordinate_y.setValue(0.5*(ymin+ymax))
                s = ""
        self.dlg.cb_centerdate.setCurrentIndex(int(len(self.TS) / 2))
unknown's avatar
unknown committed
        self.dlg.tableView_TimeSeries.resizeColumnsToContents()

    def check_enabled(self):
        D = self.dlg
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        hasTS = len(self.TS) > 0 or DEBUG
        hasTSV = len(self.BAND_VIEWS) > 0
unknown's avatar
unknown committed
        hasQGIS = qgis_available

        #D.tabWidget_viewsettings.setEnabled(hasTS)
unknown's avatar
unknown committed
        D.btn_showPxCoordinate.setEnabled(hasTS and hasTSV)
        D.btn_selectByCoordinate.setEnabled(hasQGIS)
        D.btn_selectByRectangle.setEnabled(hasQGIS)


unknown's avatar
unknown committed


    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('EnMAPBox', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

unknown's avatar
unknown committed
        self.icon_path = ':/plugins/SenseCarbon/icon.png'
unknown's avatar
unknown committed
        self.add_action(
            self.icon_path,
unknown's avatar
unknown committed
            text=self.tr(u'SenseCarbon Time Series Viewer'),
unknown's avatar
unknown committed
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(
unknown's avatar
unknown committed
                self.tr(u'&SenseCarbon Time Series Viewer'),
unknown's avatar
unknown committed
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def run(self):
        """Run method that performs all the real work"""

        #self.dlg.setWindowIcon(QIcon(self.icon_path))
        # show the GUI
        self.dlg.show()



    def scrollToDate(self, date):
        QApplication.processEvents()
        HBar = self.dlg.scrollArea_imageChips.horizontalScrollBar()
        TSDs = list(self.CHIPWIDGETS.keys())
        if len(TSDs) == 0:
            return

        #get date INDEX that is closest to requested date
        if type(date) is str:
            date = np.datetime64(date)
        assert type(date) is np.datetime64, 'type is: '+str(type(date))
        i_doi = TSDs.index(sorted(TSDs, key=lambda TSD: abs(date - TSD.getDate()))[0])
        scrollValue = int(float(i_doi+1) / len(TSDs) * HBar.maximum())
unknown's avatar
unknown committed
    def ua_showPxCoordinate_start(self):
unknown's avatar
unknown committed

        if len(self.TS) == 0:
            return
unknown's avatar
unknown committed
        D = self.dlg
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        dx = D.doubleSpinBox_subset_size_x.value() * 0.5
unknown's avatar
unknown committed
        dy = D.doubleSpinBox_subset_size_y.value() * 0.5
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        cx = D.spinBox_coordinate_x.value()
        cy = D.spinBox_coordinate_y.value()
unknown's avatar
unknown committed

        pts = [(cx - dx, cy + dy), \
               (cx + dx, cy + dy), \
               (cx + dx, cy - dy), \
               (cx - dx, cy - dy)]
unknown's avatar
unknown committed

        bb = getBoundingBoxPolygon(pts, srs=self.canvas_srs)
unknown's avatar
unknown committed
        bbWkt = bb.ExportToWkt()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        srsWkt = bb.GetSpatialReference().ExportToWkt()
unknown's avatar
unknown committed
        self.ImageChipBuffer.setBoundingBox(bb)

        D = self.dlg
unknown's avatar
unknown committed
        ratio = dx / dy
        size_px = D.spinBox_chipsize_max.value()
        if ratio > 1: #x is largest side
            size_x = size_px
            size_y = int(size_px / ratio)
        else: #y is largest
            size_y = size_px
            size_x = int(size_px * ratio)
unknown's avatar
unknown committed

unknown's avatar
unknown committed
        #get the dates of interes
        dates_of_interest = list()
        centerTSD = D.cb_centerdate.itemData(D.cb_centerdate.currentIndex())
        if centerTSD is None:
            idx = int(len(self.TS)/2)
            centerTSD = D.cb_centerdate.itemData(idx)
            D.cb_centerdate.setCurrentIndex(idx)
        centerDate = centerTSD.getDate()
        allDates = self.TS.getObservationDates()
        i_doi = allDates.index(centerDate)

unknown's avatar
unknown committed
        if D.rb_showEntireTS.isChecked():
            dates_of_interest = allDates
unknown's avatar
unknown committed
        elif D.rb_showTimeWindow.isChecked():
            i0 = max([0, i_doi-D.sb_ndates_before.value()])
            ie = min([i_doi + D.sb_ndates_after.value(), len(allDates)-1])
            dates_of_interest = allDates[i0:ie+1]
unknown's avatar
unknown committed


        diff = set(dates_of_interest)
        diff = diff.symmetric_difference(self.CHIPWIDGETS.keys())
unknown's avatar
unknown committed

        self.clearLayoutWidgets(self.ICP)
unknown's avatar
unknown committed
        self.CHIPWIDGETS.clear()

unknown's avatar
unknown committed

        #initialize image labels

        cnt_chips = 0

        TSDs_of_interest = list()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        for date in dates_of_interest:
unknown's avatar
unknown committed

            #LV = QVBoxLayout()
            #LV.setSizeConstraint(QLayout.SetNoConstraint)

            for TSD in self.TS.getTSDs(date_of_interest=date):
                TSDs_of_interest.append(TSD)
                info_label_text = '{}\n{}'.format(TSD.date, TSD.sensor.sensor_name)
                textLabel = QLabel(info_label_text)
                tt = [TSD.date,TSD.pathImg, TSD.pathMsk]
                tt = '\n'.join(str(t) for t in tt)
                textLabel.setToolTip(tt)
                self.ICP.addWidget(textLabel, 0, cnt_chips)
                viewList = list()
                j = 1
                for view in self.BAND_VIEWS:
                    bands = view.getBands(TSD.sensor)
                    imageLabel = QLabel()
                    imageLabel.setFrameShape(QFrame.StyledPanel)
                    imageLabel.setMinimumSize(size_x, size_y)
                    imageLabel.setMaximumSize(size_x+1, size_y+1)
                    imageLabel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
                    tt = [TSD.date, TSD.pathImg, 'RGB={}'.format(','.join([str(b) for b in bands]))]
                    imageLabel.setToolTip(tt)
                    viewList.append(imageLabel)
                    self.ICP.addWidget(imageLabel, j, cnt_chips)
                    j += 1

                textLabel = QLabel(info_label_text)
                textLabel.setToolTip(str(TSD))
                self.ICP.addWidget(textLabel, j, cnt_chips)

                self.CHIPWIDGETS[TSD] = viewList

                cnt_chips += 1
unknown's avatar
unknown committed

        self.scrollToDate(centerDate)

        s = ""
        #ScrollArea.show()
        #ScrollArea.horizontalScrollBar().setValue()


unknown's avatar
unknown committed

        #fill image labels
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        required_bands = dict()
        for j, view in enumerate(self.BAND_VIEWS):
                for S in view.Sensors.keys():

                    bands = set(view.getBands(S))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                    assert len(bands) == 3
                    if S not in required_bands.keys():
                        required_bands[S] = set()
                    required_bands[S] = required_bands[S].union(bands)
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        missing = set()
        for TSD in TSDs_of_interest:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            missing_bands = self.ImageChipBuffer.getMissingBands(TSD, required_bands[TSD.sensor])
            if len(missing_bands) == 0:
                self.ua_showPxCoordinate_addChips(None, TSD=TSD)
unknown's avatar
unknown committed
            else:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                missing.add((TSD, tuple(missing_bands)))

unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        missing =list(missing)
        if len(missing) > 0:
            missing = sorted(missing, key=lambda d: abs(centerDate - d[0].getDate()))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.TS.getSpatialChips_parallel(bbWkt, srsWkt, TSD_band_list=missing)
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def ua_showPxCoordinate_addChips(self, results, TSD=None):
unknown's avatar
unknown committed

        if results is not None:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            TSD, chipData = results
            self.ImageChipBuffer.addDataCube(TSD, chipData)
unknown's avatar
unknown committed

        assert TSD in self.CHIPWIDGETS.keys()
        for imageLabel, bandView in zip(self.CHIPWIDGETS[TSD], self.BAND_VIEWS):
            imageLabel.clear()
            #imageLabel.setScaledContents(True)
            rgb = self.ImageChipBuffer.getChipRGB(TSD, bandView)
            rgb2 = rgb.transpose([1,2,0]).copy('C')
            qImg = qimage2ndarray.array2qimage(rgb2)
            #img = QImage(rgb2.data, nl, ns, QImage.Format_RGB888)
            pxMap = QPixmap.fromImage(qImg)
            pxMap = pxMap.scaled(imageLabel.size(), Qt.KeepAspectRatio)
            imageLabel.setPixmap(pxMap)
            #imageLabel.update()
            imageLabel.adjustSize()
unknown's avatar
unknown committed

            s = ""
            pass
        self.ICP.layout().update()
unknown's avatar
unknown committed
        s = ""

        pass

    def clearLayoutWidgets(self, L):
        if L is not None:
            while L.count():
                w = L.takeAt(0)
                w.widget().deleteLater()
                #if w is not None:
                #    w.widget().deleteLater()
unknown's avatar
unknown committed

    def ua_addTSImages(self, files=None):
        if files is None:
            files = QFileDialog.getOpenFileNames()

        if files:
            M = self.dlg.tableView_TimeSeries.model()
            M.beginResetModel()
            self.TS.addFiles(files)
            M.endResetModel()


            if len(self.BAND_VIEWS) == 0:
                self.ua_addBandView([3, 2, 1])
                self.ua_addBandView([4, 5, 3])
unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
            self.refreshBandViews()


unknown's avatar
unknown committed

        self.check_enabled()


    def ua_addTSMasks(self, files=None):

        if files is None:
            files = QFileDialog.getOpenFileNames()

        l = len(files)
        if l > 0:
            M = self.dlg.tableView_TimeSeries.model()
            M.beginResetModel()
unknown's avatar
unknown committed
            self.TS.addMasks(files, raise_errors=False)
unknown's avatar
unknown committed
            M.endResetModel()

        self.check_enabled()



    def ua_addBandView(self, band_recommendation = [3, 2, 1]):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.BAND_VIEWS.append(BandView(self.TS))
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def refreshBandViews(self):
        self.clearLayoutWidgets(self.BVP)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        for i, BV in enumerate(self.BAND_VIEWS):
            W = QWidget()
            hl = QHBoxLayout()
            textLabel = VerticalLabel('View {}'.format(i+1))
            textLabel = QLabel('View {}'.format(i+1))
            textLabel.setToolTip('')
            textLabel.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed)
            hl.addWidget(textLabel)
            for S in self.TS.Sensors.keys():
                w = BV.getWidget(S)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                w.setMaximumSize(w.size())
                #w.setMinimumSize(w.size())
                w.setSizePolicy(QSizePolicy.Fixed,QSizePolicy.MinimumExpanding)
                #w.setBands(band_recommendation)
                hl.addWidget(w)
                s = ""

            hl.addItem(QSpacerItem(20,20,QSizePolicy.Expanding,QSizePolicy.Minimum))
            W.setLayout(hl)
            self.BVP.addWidget(W)
        self.check_enabled()
unknown's avatar
unknown committed

unknown's avatar
unknown committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def ua_removeBandView(self, w):
        self.BAND_VIEWS.remove(w)
        L = self.dlg.scrollArea_viewsWidget.layout()
        L.removeWidget(w)
        w.deleteLater()
        self.setViewNames()
unknown's avatar
unknown committed
    def ua_removeTS(self):
        #remove views

        M = self.dlg.tableView_TimeSeries.model()
        M.beginResetModel()
        self.TS.clear()
        M.endResetModel()
        self.check_enabled()

    def ua_removeTSD(self, TSDs=None):
        if TSDs is None:
            TSDs = self.getSelectedTSDs()
        assert isinstance(TSDs,list)
unknown's avatar
unknown committed

unknown's avatar
unknown committed
        M = self.dlg.tableView_TimeSeries.model()
        M.beginResetModel()
        self.TS.removeDates(TSDs)
unknown's avatar
unknown committed
        M.endResetModel()
unknown's avatar
unknown committed
        self.check_enabled()
unknown's avatar
unknown committed


    def getSelectedTSDs(self):
        TV = self.dlg.tableView_TimeSeries
        TVM = TV.model()
        return [TVM.getTimeSeriesDatumFromIndex(idx) for idx in TV.selectionModel().selectedRows()]
def disconnect_signal(signal):
    while True:
        try:
            signal.disconnect()
        except TypeError:
            break
unknown's avatar
unknown committed


unknown's avatar
unknown committed
    from scipy.misc import toimage
    toimage(data).show()

unknown's avatar
unknown committed
def run_tests():

    if False:

        pathImg = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted\2014-07-26_LC82270652014207LGN00_BOA.vrt'
        pathMsk = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted\2014-07-26_LC82270652014207LGN00_Msk.vrt'

        if False:
            TSD = TimeSeriesDatum(pathImg)
            TSD.setMask(pathMsk)
unknown's avatar
unknown committed

unknown's avatar
unknown committed

            c = [670949.883,-786288.771]

            w_x = w_y = 1000 #1km box
            srs = TSD.getSpatialReference()
            ring = ogr.Geometry(ogr.wkbLinearRing)
            import itertools
            for x,y in itertools.product([1000, -1000], repeat=2):
                ring.AddPoint(c[0]+x, c[1]+y)
            ring.AssignSpatialReference(srs)
            bb = ogr.Geometry(ogr.wkbPolygon)
            bb.AddGeometry(ring)
            bb.AssignSpatialReference(srs)
unknown's avatar
unknown committed




        def getChip3d_OLD(chips, r,g,b, range_r, range_g, range_b):

            nl, ns = chips[r].shape
            a3d = np.ndarray((3,nl,ns), dtype='float')

            rgb_idx = [r,g,b]
            ranges = [range_r, range_g, range_b]

            for i, rgb_i in enumerate(rgb_idx):
                range = ranges[i]
                data = chips[rgb_i].astype('float')
                data -= range[0]
                data *= 255./range[1]
                a3d[i,:] = data

            np.clip(a3d, 0, 255, out=a3d)

            return a3d.astype('uint8')

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        app  = QApplication([])
        main = PictureTest()
unknown's avatar
unknown committed
        main.show()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

unknown's avatar
unknown committed
        range_r = [0,500]
        range_g = [0,500]
        range_b = [0,500]

        bands = [3,2,1]
        #chipData = TSD.readSpatialChip(bb,bands=bands )
unknown's avatar
unknown committed

        #main.addNumpy(getChip3d(chipData, bands, (range_r, range_g, range_b)))
unknown's avatar
unknown committed
        app.exec_()
        exit(0)

    if False:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        dirSrcLS = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted'
        filesImgLS = file_search(dirSrcLS, '2014*_BOA.vrt')
        filesMsk = file_search(dirSrcLS, '2014*_Msk.vrt')
        TS = TimeSeries(imageFiles=filesImgLS, maskFiles=filesMsk)
unknown's avatar
unknown committed

        print(TS)
        exit(0)


unknown's avatar
unknown committed
    if True:
unknown's avatar
unknown committed
        import PyQt4.Qt
        app=PyQt4.Qt.QApplication([])
unknown's avatar
unknown committed
        S = SenseCarbon_TSV(None)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        S.run()

        if True:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            dirSrcLS = r'\\141.20.140.107\NAS_Processing\SenseCarbonProcessing\BJ_NOC\01_RasterData\02_CuttedVRT'
            dirSrcRE = r'\\141.20.140.91\SAN_RSDBrazil\RapidEye\3A'
            filesImgLS = file_search(dirSrcLS, '20*_BOA.vrt')
            filesImgRE = file_search(dirSrcRE, '*_328202.tif', recursive=True)
            #filesMsk = file_search(dirSrc, '2014*_Msk.vrt')
            #S.ua_addTSImages(files=filesImg[0:1])
            #S.ua_addTSImages(files=filesImgLS)
            #S.ua_addTSImages(files=filesImgRE)
            S.ua_loadExampleTS()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            #S.ua_addTSMasks(files=filesMsk)

        #S.ua_addView(bands=[4,5,3])

Benjamin Jakimow's avatar
Benjamin Jakimow committed

    if False:
        import qgis.core

        # supply path to where is your qgis installed

        #QgsApplication.setPrefixPath("/Applications/QGIS_2.12.app/Contents/MacOS/QGIS", True)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        # load providers
        QgsApplication.initQgis()

        a = QgsApplication([], True)

unknown's avatar
unknown committed
        S = SenseCarbon_TSV(a)
        S.run()
unknown's avatar
unknown committed
        if True:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            dirSrcLS = r'O:\SenseCarbonProcessing\BJ_NOC\01_RasterData\00_VRTs\02_Cutted'
            filesImgLS = file_search(dirSrcLS, '2014*_BOA.vrt')
            filesMsk = file_search(dirSrcLS, '2014*_Msk.vrt')
            S.ua_addTSImages(files=filesImgLS)
unknown's avatar
unknown committed
            #S.ua_addTSMasks(files=filesMsk)
unknown's avatar
unknown committed

        #S.ua_addView(bands=[4,5,3])

        a.exec_()
unknown's avatar
unknown committed
    print('Tests done')
    exit(0)


if __name__ == '__main__':
unknown's avatar
unknown committed
    run_tests()
    print('Done')