Skip to content
Snippets Groups Projects
main.py 51.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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, six
    
    from qgis.core import *
    #os.environ['PATH'] += os.pathsep + r'C:\OSGeo4W64\bin'
    
    from osgeo import gdal, ogr, osr, gdal_array
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    DEBUG = True
    
    import qgis.analysis
    
    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
    
        #import console.console_output
        #console.show_console()
        #sys.stdout = console.console_output.writeOut()
        #sys.stderr = console.console_output.writeOut()
    
    unknown's avatar
    unknown committed
    except:
    
        print('Can not find QGIS instance')
    
    unknown's avatar
    unknown committed
        qgis_available = False
    
    import numpy as np
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    import sys, bisect, multiprocessing, site
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from PyQt4.uic.Compiler.qtproxies import QtGui, QtCore
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    #abbreviations
    
    from timeseriesviewer import jp, mkdir, DIR_SITE_PACKAGES, file_search, dprint
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    site.addsitedir(DIR_SITE_PACKAGES)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    #I don't know why, but this is required to run this in QGIS
    
    #todo: still required?
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    path = os.path.abspath(jp(sys.exec_prefix, '../../bin/pythonw.exe'))
    
    unknown's avatar
    unknown committed
    if os.path.exists(path):
        multiprocessing.set_executable(path)
        sys.argv = [ None ]
    
    unknown's avatar
    unknown committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    #ensure that required non-standard modules are available
    
    unknown's avatar
    unknown committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class SpatialExtent(QgsRectangle):
        def __init__(self, crs, *args):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
            super(SpatialExtent, self).__init__(*args)
            self.mCrs = crs
    
        def setCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
            self.mCrs = crs
    
        def crs(self):
            return self.mCrs
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def toCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
    
            box = QgsRectangle(self)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if self.mCrs != crs:
                trans = QgsCoordinateTransform(self.mCrs, crs)
                box = trans.transformBoundingBox(box)
            return SpatialExtent(crs, box)
    
    
        def __copy__(self):
            return SpatialExtent(self.crs(), QgsRectangle(self))
    
        def combineExtentWith(self, *args):
            if args is None:
                return
            elif isinstance(args[0], SpatialExtent):
                extent2 = args[0].toCrs(self.crs())
                self.combineExtentWith(QgsRectangle(extent2))
            else:
                super(SpatialExtent, self).combineExtentWith(*args)
    
        def setCenter(self, centerPoint, crs=None):
    
            if crs and crs != self.crs():
                trans = QgsCoordinateTransform(crs, self.crs())
                centerPoint = trans.transform(centerPoint)
    
            delta = centerPoint - self.center()
            self.setXMaximum(self.xMaximum() + delta.x())
            self.setXMinimum(self.xMinimum() + delta.x())
            self.setYMaximum(self.yMaximum() + delta.y())
            self.setYMinimum(self.yMinimum() + delta.y())
    
    
        def __cmp__(self, other):
            if other is None: return 1
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            s = ""
    
    
        def __eq__(self, other):
            s = ""
    
        def __sub__(self, other):
            raise NotImplementedError()
    
        def __mul__(self, other):
            raise NotImplementedError()
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from timeseriesviewer.ui.widgets import *
    from timeseriesviewer.timeseries import TimeSeries, TimeSeriesDatum, SensorInstrument
    
    
    
    unknown's avatar
    unknown committed
    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):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    return self.TS.data[i]
    
    unknown's avatar
    unknown committed
    
            return None
    
    
    
        def data(self, index, role = Qt.DisplayRole):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if role is None or not index.isValid():
    
    unknown's avatar
    unknown committed
                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:
                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
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class TimeSeriesDatumViewManager(QObject):
    
        def __init__(self, timeSeriesViewer):
            assert isinstance(timeSeriesViewer, TimeSeriesViewer)
            super(TimeSeriesDatumViewManager, self).__init__()
    
            self.TSV = timeSeriesViewer
    
            self.TSDViews = list()
            self.bandViewMananger = self.TSV.bandViewManager
            self.bandViewMananger.sigBandViewAdded.connect(self.addBandView)
            self.bandViewMananger.sigBandViewRemoved.connect(self.removeBandView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.bandViewMananger.sigBandViewVisibility.connect(self.setBandViewVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
            self.setExtent(self.TSV.TS.getMaxExtent())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.setMaxTSDViews()
            self.setTimeSeries(self.TSV.TS)
            self.L = self.TSV.ui.scrollAreaSubsetContent.layout()
            self.setSubsetSize(QSize(100,50))
    
        def setSubsetSize(self, size):
            assert isinstance(size, QSize)
            self.subsetSize = size
            for tsdv in self.TSDViews:
                tsdv.setSubsetSize(size)
            self.adjustScrollArea()
    
    
        def adjustScrollArea(self):
    
            m = self.L.contentsMargins()
            n = len(self.TSDViews)
            if n > 0:
                refTSDView = self.TSDViews[0]
                size = refTSDView.ui.size()
    
                w = n * size.width() + (n-1) * (m.left()+ m.right())
                h = max([refTSDView.ui.minimumHeight() + m.top() + m.bottom(),
                         self.TSV.ui.scrollAreaSubsets.height()-25])
    
                self.L.parentWidget().setFixedSize(w,h)
    
    
        def setTimeSeries(self,TS):
            assert isinstance(TS, TimeSeries)
            self.TS = TS
            self.TS.sigTimeSeriesDatumAdded.connect(self.createTSDView)
    
    
        def setMaxTSDViews(self, n=-1):
            self.nMaxTSDViews = n
            #todo: remove views
    
        def setExtent(self, extent):
            self.extent = extent
    
            if extent:
                assert isinstance(extent, SpatialExtent)
                tsdviews = sorted(self.TSDViews, key=lambda t:t.TSD)
                for tsdview in tsdviews:
                    tsdview.setSpatialExtent(extent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def navToDOI(self, TSD):
            assert isinstance(TSD, TimeSeriesDatum)
            #get widget related to TSD
            tsdviews = [t for t in self.TSDViews if t.TSD == TSD]
            if len(tsdviews) > 0:
                i = self.TSDViews.index(tsdviews[0])+1.5
                n = len(self.TSDViews)
    
                scrollBar = self.TSV.ui.scrollAreaSubsets.horizontalScrollBar()
                smin = scrollBar.minimum()
                smax = scrollBar.maximum()
                v = smin + (smax - smin) * float(i) / n
                scrollBar.setValue(int(round(v)))
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setBandViewVisibility(self, bandView, isVisible):
            assert isinstance(bandView, BandView)
            assert isinstance(isVisible, bool)
    
            for tsdv in self.TSDViews:
                tsdv.ui.setBandViewVisibility(isVisible)
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def addBandView(self, bandView):
            assert isinstance(bandView, BandView)
    
            w = self.L.parentWidget()
            w.setUpdatesEnabled(False)
    
            for tsdv in self.TSDViews:
                tsdv.ui.setUpdatesEnabled(False)
    
            for tsdv in self.TSDViews:
                tsdv.insertBandView(bandView)
    
            for tsdv in self.TSDViews:
                tsdv.ui.setUpdatesEnabled(True)
    
            w.setUpdatesEnabled(True)
    
    
    
        def removeBandView(self, bandView):
            assert isinstance(bandView, BandView)
            for tsdv in self.TSDViews:
                tsdv.removeBandView(bandView)
    
    
    
        def createTSDView(self, TSD):
            assert isinstance(TSD, TimeSeriesDatum)
            tsdView = TimeSeriesDatumView(TSD)
            tsdView.setSubsetSize(self.subsetSize)
    
            if self.extent:
                tsdView.setSpatialExtent(self.extent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.addTSDView(tsdView)
    
    
        def removeTSD(self, TSD):
            assert isinstance(TSDV, TimeSeriesDatum)
            tsdvs = [tsdv for tsdv in self.TSDViews if tsdv.TSD == TSD]
            assert len(tsdvs) == 1
            self.removeTSDView(tsdvs[0])
    
        def removeTSDView(self, TSDV):
            assert isinstance(TSDV, TimeSeriesDatumView)
            self.TSDViews.remove(TSDV)
    
        def addTSDView(self, TSDV):
            assert isinstance(TSDV, TimeSeriesDatumView)
    
            if len(self.TSDViews) < 10:
                pass
    
            bisect.insort(self.TSDViews, TSDV)
    
            TSDV.ui.setParent(self.L.parentWidget())
            self.L.addWidget(TSDV.ui)
    
            self.adjustScrollArea()
            #self.TSV.ui.scrollAreaSubsetContent.update()
            #self.TSV.ui.scrollAreaSubsets.update()
            s = ""
    
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        sigAddBandView = pyqtSignal(object)
        sigRemoveBandView = pyqtSignal(object)
    
        sigHideBandView = pyqtSignal(bool)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, recommended_bands=None, parent=None, showSensorNames=True):
    
            super(BandView, self).__init__()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui = BandViewUI(parent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.create()
    
            #forward actions with reference to this band view
            self.ui.actionAddBandView.triggered.connect(lambda : self.sigAddBandView.emit(self))
            self.ui.actionRemoveBandView.triggered.connect(lambda: self.sigRemoveBandView.emit(self))
    
            self.ui.actionHideBandView.toggled.connect(self.sigHideBandView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.sensorViews = collections.OrderedDict()
            self.mShowSensorNames = showSensorNames
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setTitle(self, title):
            self.ui.labelViewName.setText(title)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def showSensorNames(self, b):
            assert isinstance(b, bool)
            self.mShowSensorNames = b
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for s,w in self.sensorViews.items():
                w.showSensorName(b)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def removeSensor(self, sensor):
            assert type(sensor) is SensorInstrument
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if sensor in self.sensorViews.keys():
                self.sensorViews[sensor].close()
                self.sensorViews.pop(sensor)
                return True
            else:
                return False
    
        def hasSensor(self, sensor):
            assert type(sensor) is SensorInstrument
            return sensor in self.sensorViews.keys()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def addSensor(self, sensor):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            :param sensor:
            :return:
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert type(sensor) is SensorInstrument
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert sensor not in self.sensorViews.keys()
    
            w = BandViewRenderSettings(sensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            w.showSensorName(self.mShowSensorNames)
            self.sensorViews[sensor] = w
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            l = self.ui.sensorList.layout()
            i = l.count() #last widget is a spacer
            l.insertWidget(i-1, w.ui)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def getSensorWidget(self, sensor):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert type(sensor) is SensorInstrument
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.sensorViews[sensor]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class BandViewManager(QObject):
    
        sigSensorAdded = pyqtSignal(SensorInstrument)
        sigSensorRemoved = pyqtSignal(SensorInstrument)
        sigBandViewAdded = pyqtSignal(BandView)
        sigBandViewRemoved = pyqtSignal(BandView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        sigBandViewVisibility = pyqtSignal(BandView, bool)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def __init__(self, timeSeriesViewer):
            assert isinstance(timeSeriesViewer, TimeSeriesViewer)
            super(BandViewManager, self).__init__()
    
            self.TSV = timeSeriesViewer
            self.bandViews = []
    
        def removeSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
    
            removed = False
            for view in self.bandViews:
                removed = removed and view.removeSensor(sensor)
    
            if removed:
                self.sigSensorRemoved(sensor)
    
    
        def createBandView(self):
            bandView = BandView(parent=self.TSV.ui.scrollAreaBandViewsContent, showSensorNames=False)
            bandView.sigAddBandView.connect(self.createBandView)
            bandView.sigRemoveBandView.connect(self.removeBandView)
    
            bandView.sigHideBandView.connect(lambda b: self.sigBandViewVisibility.emit(bandView, b))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.addBandView(bandView)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setBandViewVisibility(self, bandView, isVisible):
            assert isinstance(bandView, BandView)
            assert isinstance(isVisible, bool)
    
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def addBandView(self, bandView):
            assert isinstance(bandView, BandView)
            l = self.TSV.BVP
            self.bandViews.append(bandView)
            l.addWidget(bandView.ui)
            n = len(self)
    
            for sensor in self.TSV.TS.Sensors:
                bandView.addSensor(sensor)
    
            bandView.showSensorNames(n == 1)
            bandView.setTitle('#{}'.format(n))
            self.sigBandViewAdded.emit(bandView)
    
        def removeBandView(self, bandView):
            assert isinstance(bandView, BandView)
            self.bandViews.remove(bandView)
            self.TSV.BVP.removeWidget(bandView.ui)
            bandView.ui.close()
            #self.TSV.ui.scrollAreaBandViewsContent.update()
    
            if len(self.bandViews) > 0:
                self.bandViews[0].showSensorNames(True)
                for i, bv in enumerate(self.bandViews):
                    bv.setTitle('#{}'.format(i + 1))
    
            self.sigBandViewRemoved.emit(bandView)
    
        def __len__(self):
            return len(self.bandViews)
    
        def __iter__(self):
            return iter(self.bandViews)
    
        def __getitem__(self, key):
            return self.bandViews[key]
    
        def __contains__(self, bandView):
            return bandView in self.bandViews
    
    
    class TimeSeriesDatumView(QObject):
        def __init__(self, TSD, parent=None):
    
            super(TimeSeriesDatumView, self).__init__()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui = TimeSeriesDatumViewUI(parent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.create()
    
            self.TSD = None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.bandViewCanvases = dict()
            self.bandViewOrder = []
    
            self.setTimeSeriesDatum(TSD)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.L = self.ui.layout()
            self.wOffset = self.L.count()-1
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.setSubsetSize(QSize(150,100))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setBandViewVisibility(self, bandView, isVisible):
            self.bandViewCanvases[bandView].setVisible(isVisible)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def setSpatialExtent(self, extent):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(extent, SpatialExtent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for c in self.bandViewCanvases.values():
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                c.setExtent(extent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                c.setDestinationCrs(extent.crs())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def setSubsetSize(self, size):
            assert isinstance(size, QSize)
            assert size.width() > 5 and size.height() > 5
            self.subsetSize = size
            m = self.L.contentsMargins()
    
            self.ui.labelTitle.setFixedWidth(size.width())
            self.ui.line.setFixedWidth(size.width())
    
            #apply new subset size to existing canvases
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for c in self.bandViewCanvases.values():
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                c.setFixedSize(size)
    
            self.ui.setFixedWidth(size.width() + 2*(m.left() + m.right()))
            n = len(self.bandViewCanvases)
            #todo: improve size forecast
            self.ui.setMinimumHeight((n+1) * size.height())
    
    
        def setTimeSeriesDatum(self, TSD):
            assert isinstance(TSD, TimeSeriesDatum)
            self.TSD = TSD
            self.ui.labelTitle.setText(str(TSD.date))
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for c in self.bandViewCanvases.values():
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                c.setLayer(self.TSD.pathImg)
    
        def removeBandView(self, bandView):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.bandViewOrder.remove(bandView)
            canvas = self.bandViewCanvases[bandView]
            self.L.removeWidget(canvas)
            canvas.close()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def insertBandView(self, bandView, i=-1):
            assert isinstance(bandView, BandView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert bandView not in self.bandViewOrder
    
            assert len(self.bandViewCanvases) == len(self.bandViewOrder)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            assert i >= -1 and i <= len(self.bandViewOrder)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if i == -1:
                i = len(self.bandViewCanvases)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            canvas = BandViewMapCanvas(self.ui)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            canvas.setLayer(self.TSD.pathImg)
            canvas.setFixedSize(self.subsetSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.bandViewCanvases[bandView] = canvas
            self.bandViewOrder.insert(i, bandView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.L.insertWidget(self.wOffset + i, canvas)
    
        def __lt__(self, other):
    
            return self.TSD < other.TSD
    
    
    
        def __init__(self, TSD, renderer, destinationId=None):
            assert isinstance(TSD, TimeSeriesDatum)
            assert isinstance(renderer, QgsRasterRenderer)
    
            self.TSD = TSD
            self.renderer = renderer
            self.destinationId = destinationId
    
        def __eq__(self, other):
            if not isinstance(other, RenderJob):
                return False
            return self.TSD == other.TSD and \
                   self.renderer == other.renderer and \
                   self.destinationId == other.destinationId
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class PixmapBuffer(QObject):
        sigProgress = pyqtSignal(int, int)
        sigPixmapCreated = pyqtSignal(RenderJob, QPixmap)
    
        def __init__(self):
            super(PixmapBuffer, self).__init__()
    
            self.extent = None
            self.crs = None
            self.size = None
            self.nWorkersMax = 1
            self.Workers = []
            self.PIXMAPS = dict()
            self.JOBS = []
            self.nTotalJobs = -1
            self.nJobsDone = -1
    
        def addWorker(self):
            w = Worker()
            w.setCrsTransformEnabled(True)
            w.sigPixmapCreated.connect(self.newPixmapCreated)
            if self.isValid():
                self.setWorkerProperties(w)
            self.Workers.append(w)
    
        def setWorkerProperties(self, worker):
            assert isinstance(worker, Worker)
            worker.setFixedSize(self.size)
            worker.setDestinationCrs(self.crs)
            worker.setExtent(self.extent)
            worker.setCenter(self.extent.center())
    
        def newPixmapCreated(self, renderJob, pixmap):
            self.JOBS.remove(renderJob)
            self.nJobsDone += 1
            self.sigPixmapCreated.emit(renderJob, pixmap)
            self.sigProgress.emit(self.nJobsDone, self.nTotalJobs)
    
        def isValid(self):
            return self.extent != None and self.crs != None and self.size != None
    
        def setExtent(self, extent, crs, maxPx):
            self.stopRendering()
            assert isinstance(extent, QgsRectangle)
            assert isinstance(crs, QgsCoordinateReferenceSystem)
            assert isinstance(maxPx, int)
            ratio = extent.width() / extent.height()
            if ratio < 1:  # x is largest side
                size = QSize(maxPx, int(maxPx / ratio))
            else:  # y is largest
                size = QSize(int(maxPx * ratio), maxPx)
    
            self.crs = crs
            self.size = size
            self.extent = extent
            for w in self.Workers:
                self.setWorkerProperties(w)
            return size
    
        def stopRendering(self):
            for w in self.Workers:
                w.stopRendering()
                w.clear()
            while len(self.JOBS) > 0:
                self.JOBS.pop(0)
            self.sigProgress.emit(0, 0)
    
        def loadSubsets(self, jobs):
            for j in jobs:
                assert isinstance(j, RenderJob)
    
            self.stopRendering()
    
            self.JOBS.extend(jobs)
            self.nTotalJobs = len(self.JOBS)
            self.nJobsDone = 0
            self.sigProgress.emit(0, self.nTotalJobs)
    
            if len(self.Workers) == 0:
                self.addWorker()
    
            #split jobs to number of workers
            i = 0
            chunkSize = int(len(self.JOBS) / len(self.Workers))
            assert chunkSize > 0
            for i in range(0, len(self.Workers), chunkSize):
                worker = self.Workers[i]
                j = min(i+chunkSize, len(self.JOBS))
                worker.startLayerRendering(self.JOBS[i:j])
    
    class Worker(QgsMapCanvas):
    
        sigPixmapCreated = pyqtSignal(RenderJob, QPixmap)
    
    
        def __init__(self, *args, **kwds):
            super(Worker,self).__init__(*args, **kwds)
            self.reg = QgsMapLayerRegistry.instance()
            self.painter = QPainter()
            self.renderJobs = list()
            self.mapCanvasRefreshed.connect(self.createPixmap)
    
        def isBusy(self):
            return len(self.renderJobs) != 0
    
        def createPixmap(self, *args):
            if len(self.renderJobs) > 0:
                pixmap = QPixmap(self.size())
                self.painter.begin(pixmap)
                self.map().paint(self.painter)
                self.painter.end()
                assert not pixmap.isNull()
                job = self.renderJobs.pop(0)
                self.sigPixmapCreated.emit(job, pixmap)
                self.startSingleLayerRendering()
    
        def stopLayerRendering(self):
            self.stopRendering()
            del self.renderJobs[:]
            assert self.isBusy() is False
    
        def startLayerRendering(self, renderJobs):
            assert isinstance(renderJobs, list)
            self.renderJobs.extend(renderJobs)
            self.startSingleLayerRendering()
    
    
    
        def startSingleLayerRendering(self):
    
            if len(self.renderJobs) > 0:
                renderJob = self.renderJobs[0]
                assert isinstance(renderJob, RenderJob)
                #mapLayer = QgsRasterLayer(renderJob.TSD.pathImg)
                mapLayer = renderJob.TSD.lyrImg
                mapLayer.setRenderer(renderJob.renderer)
                dprint('QgsMapLayerRegistry count: {}'.format(self.reg.count()))
                self.reg.addMapLayer(mapLayer)
    
                lyrSet = [QgsMapCanvasLayer(mapLayer)]
                self.setLayerSet(lyrSet)
    
                #todo: add crosshair
                self.refreshAllLayers()
    
    unknown's avatar
    unknown committed
    
    
    class ImageChipLabel(QLabel):
    
        clicked = pyqtSignal(object, object)
    
    
        def __init__(self, time_series_viewer, TSD, renderer):
            assert isinstance(time_series_viewer, TimeSeriesViewer)
            assert isinstance(TSD, TimeSeriesDatum)
            assert isinstance(renderer, QgsRasterRenderer)
    
            super(ImageChipLabel, self).__init__(time_series_viewer.ui)
    
            self.TSV = time_series_viewer
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.TSD = TSD
            self.bn = os.path.basename(self.TSD.pathImg)
    
            self.setContextMenuPolicy(Qt.DefaultContextMenu)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.setFrameShape(QFrame.StyledPanel)
            self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
    
            tt = ['Date: {}'.format(TSD.date) \
                 ,'Name: {}'.format(self.bn) \
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.setToolTip(list2str(tt))
    
    
        def mouseReleaseEvent(self, event):
    
    	    self.clicked.emit(self, event)
    
    
        def contextMenuEvent(self, event):
            menu = QMenu()
            #add general options
    
            action = menu.addAction('Copy to clipboard')
            action.triggered.connect(lambda : QApplication.clipboard().setPixmap(self.pixmap()))
    
    
            #add QGIS specific options
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                action = menu.addAction('Add {} to QGIS layers'.format(self.bn))
                action.triggered.connect(lambda : qgis_add_ins.add_QgsRasterLayer(self.iface, self.TSD.pathImg, self.bands))
    
    unknown's avatar
    unknown committed
    
    
    
    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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        if isinstance(srs, QgsCoordinateReferenceSystem):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            _crs = osr.SpatialReference()
            _crs.ImportFromWkt(srs.toWkt())
            bb.AssignSpatialReference(_crs)
        return bb
    
    unknown's avatar
    unknown committed
    def getDS(ds):
        if type(ds) is not gdal.Dataset:
            ds = gdal.Open(ds)
        return ds
    
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
    
    
    def getBandNames(lyr):
        assert isinstance(lyr, QgsRasterLayer)
        dp = lyr.dataProvider()
        assert isinstance(dp, QgsRasterDataProvider)
        if str(dp.name()) == 'gdal':
            s = ""
        else:
            return lyr
    
    
    unknown's avatar
    unknown committed
    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))
    
    
    
    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)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    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
    
    
        def getChipArray(self, TSD, band_view, mode='rgb'):
            assert mode in ['rgb', 'bgr']
            bands = band_view.getBands(TSD.sensor)
            band_ranges = band_view.getRanges(TSD.sensor)
            nb = len(bands)
            assert nb == 3 and nb == len(band_ranges)
            assert TSD in self.data.keys(), 'Time Series Datum {} is not in buffer'.format(TSD.getDate())
            chipData = self.data[TSD]
            for b in bands:
                assert b in chipData.keys()