Skip to content
Snippets Groups Projects
main.py 51.5 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, 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

    def toCrs(self, crs):
        assert isinstance(crs, QgsCoordinateReferenceSystem)
        box = super(SpatialExtent, self)
        if self.mCrs != crs:
            trans = QgsCoordinateTransform(self.mCrs, crs)
            box = trans.transformBoundingBox(box)
        return SpatialExtent(crs, box)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __radd__(self, other):
        s = ""

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

        extent = self.TSV.TS.getMaxExtent()
        if extent:
            self.setExtent(extent)

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
        assert isinstance(extent, QgsRectangle)
        tsdviews = sorted(self.TSDViews, key=lambda t:t.TSD)
        for tsdview in tsdviews:
            tsdview.setSpatialExtent(extent)

    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)
        tsdView.setSpatialExtent(self.extent)
        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)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    sigSetVisibility = 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))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.actionToggleVisibility.toggled.connect(self.sigSetVisibility)
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()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        w = ImageChipViewSettings(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)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        bandView.sigSetVisibility.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
        self.setTimeSeriesDatum(TSD)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.bandViewCanvases = dict()
        self.bandViewOrder = []
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) == self.bandViewOrder

        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()



        nl, ns = chipData[bands[0]].shape

        dtype= 'uint8'
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        array_data = np.ndarray((nl,ns, nb), dtype=dtype)

        if mode == 'rgb':
            ch_dst = [0,1,2]
        elif mode == 'bgr':
            # r -> dst channel 2
            # g -> dst channel 1
            # b -> dst channel 0
            ch_dst = [2,1,0]
        for i, i_dst in enumerate(ch_dst):

            offset = band_ranges[i][0]
            scale = 255./band_ranges[i][1]

            res = pg.rescaleData(chipData[bands[i]], scale, offset, dtype='float')
            np.clip(res, 0, 255, out=res)
            array_data[:,:,i_dst] = res

        return array_data


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