Skip to content
Snippets Groups Projects
main.py 44.5 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
        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
    
    from timeseriesviewer.timeseries import *
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    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):
    
        """
        Object to keep QgsRectangle and QgsCoordinateReferenceSystem together
        """
    
        @staticmethod
        def fromMapCanvas(mapCanvas):
            assert isinstance(mapCanvas, QgsMapCanvas)
            extent = mapCanvas.extent()
            crs = mapCanvas.mapSettings().destinationCrs()
            return SpatialExtent(crs, extent)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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 = 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()
    
    
        def upperLeft(self):
            return self.xMinimum(), self.yMaximum()
    
        def lowerRight(self):
            return self.xMaximum(), self.yMinimum()
    
        def __repr__(self):
    
            return '{} {} {}'.format(self.upperLeft(), self.lowerRight(), self.crs().authid())
    
    
    class TsvMimeDataUtils(QObject):
        def __init__(self, mimeData):
            assert isinstance(mimeData, QMimeData)
            super(TsvMimeDataUtils, self).__init__()
    
            self.mimeData = mimeData
    
            self.xmlDoc = QDomDocument()
    
            if self.mimeData.hasText():
                self.xmlDoc.setContent(self.mimeData.text())
            self.xmlRoot = self.xmlDoc.documentElement()
            pass
    
        def hasRasterStyle(self):
            if self.xmlRoot.tagName() == 'qgis':
                elem = self.xmlRoot.elementsByTagName('rasterrenderer')
                return elem.count() != 0
    
            return False
    
    
        def rasterStyle(self, qgisDataType):
    
            elem = self.xmlRoot.elementsByTagName('rasterrenderer').item(0).toElement()
            type = str(elem.attribute('type'))
            from qgis.core import QGis, QgsContrastEnhancement
    
            def bandSettings(colorName):
                band = int(elem.attribute(colorName + 'Band'))
                ceNode = elem.elementsByTagName(colorName + 'ContrastEnhancement').item(0)
                vMin = float(ceNode.firstChildElement('minValue').firstChild().nodeValue())
                vMax = float(ceNode.firstChildElement('maxValue').firstChild().nodeValue())
                ceName = ceNode.firstChildElement('algorithm').firstChild().nodeValue()
                ceAlg = QgsContrastEnhancement.contrastEnhancementAlgorithmFromString(ceName)
                ce = QgsContrastEnhancement(qgisDataType)
                ce.setContrastEnhancementAlgorithm(ceAlg)
                ce.setMinimumValue(vMin)
                ce.setMaximumValue(vMax)
                return band, ce
    
            style = None
            if type == 'multibandcolor':
                    A = int(elem.attribute('alphaBand'))
                    O = int(elem.attribute('opacity'))
                    R, ceR = bandSettings('red')
                    G, ceG = bandSettings('green')
                    B, ceB = bandSettings('blue')
    
                    style = QgsMultiBandColorRenderer(None, R, G, B)
                    style.setRedContrastEnhancement(ceR)
                    style.setGreenContrastEnhancement(ceG)
                    style.setBlueContrastEnhancement(ceB)
    
            elif type == 'singlebandgrey':
    
                pass
    
            return style
    
    class QgisTsvBridge(QObject):
        """
        Class to control interactions between TSV and a running QGIS instance
        """
        _instance = None
    
    
        @staticmethod
        def instance():
            return QgisTsvBridge._instance
    
        def __init__(self, iface, TSV_UI):
            super(QgisTsvBridge, self).__init__()
            assert QgisTsvBridge._instance is None
            assert isinstance(iface, QgisInterface)
            self.iface = iface
            self.ui = TSV_UI
    
            from timeseriesviewer.ui.widgets import TimeSeriesViewerUI
            assert isinstance(self.ui, TimeSeriesViewerUI)
            self.cbQgsVectorLayer = self.ui.dockRendering.cbQgsVectorLayer
            self.gbQgsVectorLayer = self.ui.dockRendering.gbQgsVectorLayer
            self.qgsMapCanvas = self.iface.mapCanvas()
    
            assert isinstance(self.cbQgsVectorLayer, QgsMapLayerComboBox)
            assert isinstance(self.gbQgsVectorLayer, QgsCollapsibleGroupBox)
            assert isinstance(self.qgsMapCanvas, QgsMapCanvas)
    
            self.cbSyncQgsMapCenter = self.ui.dockNavigation.cbSyncQgsMapCenter
            self.cbSyncQgsMapExtent = self.ui.dockNavigation.cbSyncQgsMapExtent
            self.cbSyncQgsCRS = self.ui.dockNavigation.cbSyncQgsCRS
    
            for cb in [self.cbSyncQgsMapCenter, self.cbSyncQgsMapExtent, self.cbSyncQgsCRS]:
                assert isinstance(cb, QCheckBox)
                self.cbSyncQgsMapExtent.clicked.connect(lambda : self.onSpatialSyncChanged())
    
            self.cbQgsVectorLayer.layerChanged.connect(self.onQgsVectorLayerChanged)
        def onSpatialSyncChanged(self):
            dprint('QgisTsvBridge: spatial sync changed')
    
        def onQgsVectorLayerChanged(self, lyr):
            dprint('QgisTsvBridge: selected Qgs Vector layer changed')
    
        def extent(self):
            assert isinstance(self.qgsMapCanvas, QgsMapCanvas)
            return SpatialExtent.fromMapCanvas(self.qgsMapCanvas)
    
    
        def getVectorLayerRepresentation(self):
            if self.ui.gbQgsVectorLayer.isChecked():
    
                lyr = self.cbQgsVectorLayer.currentLayer()
    
                alpha = self.ui.sliderQgsVectorTransparency.value()
                return lyr
            else:
                return None
    
    
        def syncExtent(self, isChecked):
            if isChecked:
    
                self.cbSyncQgsMapCenter.setEnabled(False)
                self.cbSyncQgsMapCenter.blockSignals(True)
                self.cbSyncQgsMapCenter.setChecked(True)
                self.cbSyncQgsMapCenter.blockSignals(False)
    
                self.cbSyncQgsMapCenter.setEnabled(True)
    
            self.qgsSyncStateChanged()
    
        def qgsSyncState(self):
            return (self.cbSyncQgsMapCenter.isChecked(),
                    self.cbSyncQgsMapExtent.isChecked(),
                    self.cbSyncQgsCRS.isChecked())
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    unknown's avatar
    unknown committed
    
    
    
    unknown's avatar
    unknown committed
    
    
        sigRemoveMapView = pyqtSignal(object)
        sigMapViewVisibility = pyqtSignal(bool)
        sigTitleChanged = pyqtSignal(str)
        sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
    
        sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
    
    
        def __init__(self, mapViewCollection, recommended_bands=None, parent=None):
            super(MapView, self).__init__()
            assert isinstance(mapViewCollection, MapViewCollection)
            self.MVC = mapViewCollection
    
            from timeseriesviewer.ui.widgets import MapViewDefinitionUI
    
            self.ui = MapViewDefinitionUI(self, parent=parent)
            self.ui.create()
    
            self.setVisibility(True)
    
            #forward actions with reference to this band view
            self.spatialExtent = None
            self.ui.actionRemoveMapView.triggered.connect(lambda: self.sigRemoveMapView.emit(self))
            self.ui.actionApplyStyles.triggered.connect(self.applyStyles)
            self.ui.sigShowMapView.connect(lambda: self.sigMapViewVisibility.emit(True))
            self.ui.sigHideMapView.connect(lambda: self.sigMapViewVisibility.emit(False))
            self.sensorViews = collections.OrderedDict()
    
            self.mSpatialExtent = None
    
        def applyStyles(self):
            for sensorView in self.sensorViews.values():
                sensorView.applyStyle()
            s = ""
    
    
        def setVisibility(self, isVisible):
            self.ui.setVisibility(isVisible)
    
        def setSpatialExtent(self, extent):
            assert isinstance(extent, SpatialExtent)
            self.mSpatialExtent = extent
            self.sigSpatialExtentChanged.emit(extent)
    
        def visibility(self):
            return self.ui.visibility()
    
        def setTitle(self, title):
            self.mTitle = title
    
            #self.ui.setTitle('Map View' + title)
    
            self.sigTitleChanged.emit(self.mTitle)
    
        def title(self):
            return self.mTitle
    
    unknown's avatar
    unknown committed
    
    
    
        def removeSensor(self, sensor):
            assert type(sensor) is SensorInstrument
            if sensor in self.sensorViews.keys():
                self.sensorViews[sensor].close()
                self.sensorViews.pop(sensor)
                self.ui.adjustSize()
                return True
            else:
    
    unknown's avatar
    unknown committed
                return False
    
    
        def hasSensor(self, sensor):
            assert type(sensor) is SensorInstrument
            return sensor in self.sensorViews.keys()
    
        def addSensor(self, sensor):
            """
            :param sensor:
            :return:
            """
            assert type(sensor) is SensorInstrument
            assert sensor not in self.sensorViews.keys()
    
            from timeseriesviewer.ui.widgets import MapViewSensorSettings
    
            w = MapViewSensorSettings(sensor)
    
            #w.showSensorName(False)
            self.sensorViews[sensor] = w
            l = self.ui.sensorList
            i = l.count()
            l.addWidget(w.ui)
            from timeseriesviewer.ui.widgets import maxWidgetSizes
    
    
            s = ""
    
    unknown's avatar
    unknown committed
    
    
    
        def getSensorWidget(self, sensor):
            assert type(sensor) is SensorInstrument
            return self.sensorViews[sensor]
    
    class TimeSeriesDatumView(QObject):
    
    unknown's avatar
    unknown committed
    
    
        sigExtentsChanged = pyqtSignal(SpatialExtent)
        sigRenderProgress = pyqtSignal(int,int)
        sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
        sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
        sigVisibilityChanged = pyqtSignal(bool)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, TSD, timeSeriesDateViewCollection, mapViewCollection, parent=None):
            assert isinstance(TSD, TimeSeriesDatum)
            assert isinstance(timeSeriesDateViewCollection, TimeSeriesDateViewCollection)
            assert isinstance(mapViewCollection, MapViewCollection)
    
            super(TimeSeriesDatumView, self).__init__()
    
            from timeseriesviewer.ui.widgets import TimeSeriesDatumViewUI
            self.ui = TimeSeriesDatumViewUI(parent=parent)
    
            self.ui.create()
    
            self.L = self.ui.layout()
            self.wOffset = self.L.count()-1
            self.minHeight = self.ui.height()
            self.minWidth = 50
            self.renderProgress = dict()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.scrollArea = timeSeriesDateViewCollection.scrollArea
    
            self.Sensor = self.TSD.sensor
            self.TSD.sigVisibilityChanged.connect(self.setVisibility)
            self.ui.labelTitle.setText(str(TSD.date))
            self.MVC = mapViewCollection
            self.TSDVC = timeSeriesDateViewCollection
            self.mapCanvases = dict()
            self.setSubsetSize(QSize(50, 50))
    
        def setVisibility(self, b):
            self.ui.setVisible(b)
            self.sigVisibilityChanged.emit(b)
    
        def activateMapTool(self, key):
            for c in self.mapCanvases.values():
                c.activateMapTool(key)
    
        def setMapViewVisibility(self, bandView, isVisible):
            self.mapCanvases[bandView].setVisible(isVisible)
    
        def setSpatialExtent(self, spatialExtent):
            assert isinstance(spatialExtent, SpatialExtent)
    
            for c in self.mapCanvases.values():
                c.setSpatialExtent(spatialExtent)
    
    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
    
    
            self.ui.labelTitle.setFixedWidth(size.width())
            self.ui.line.setFixedWidth(size.width())
    
            #apply new subset size to existing canvases
    
            for canvas in self.mapCanvases.values():
                canvas.setFixedSize(size)
            self.adjustBaseMinSize()
    
    
        def adjustBaseMinSize(self):
            self.ui.setFixedSize(self.ui.sizeHint())
    
        def removeMapView(self, mapView):
            canvas = self.mapCanvases.pop(mapView)
            self.L.removeWidget(canvas)
            canvas.close()
            self.adjustBaseMinSize()
    
        def redraw(self):
            if self.ui.isVisible():
                for c in self.mapCanvases.values():
                    if c.isVisible():
                        c.refreshAllLayers()
    
        def insertMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            i = self.MVC.index(mapView)
    
    
            from timeseriesviewer.ui.widgets import TsvMapCanvas
    
            canvas = TsvMapCanvas(self, mapView, parent=self.ui)
    
            canvas.setFixedSize(self.subsetSize)
            canvas.extentsChanged.connect(lambda : self.sigExtentsChanged.emit(canvas.spatialExtent()))
            canvas.renderStarting.connect(lambda : self.sigLoadingStarted.emit(mapView, self.TSD))
            canvas.mapCanvasRefreshed.connect(lambda: self.sigLoadingFinished.emit(mapView, self.TSD))
    
            canvas.sigShowProfiles.connect(mapView.sigShowProfiles.emit)
    
            self.mapCanvases[mapView] = canvas
            self.L.insertWidget(self.wOffset + i, canvas)
            canvas.refreshMap()
            self.adjustBaseMinSize()
            return canvas
    
        def __lt__(self, other):
    
            return self.TSD < other.TSD
    
        def __cmp__(self, other):
            return cmp(self.TSD, other.TSD)
    
    
    
    class SpatialTemporalVisualization(QObject):
        """
    
        """
    
        sigLoadingStarted = pyqtSignal(TimeSeriesDatumView, MapView)
        sigLoadingFinished = pyqtSignal(TimeSeriesDatumView, MapView)
        sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
        sigShowMapLayerInfo = pyqtSignal(dict)
    
    
        def __init__(self, timeSeriesViewer):
            assert isinstance(timeSeriesViewer, TimeSeriesViewer)
            super(SpatialTemporalVisualization, self).__init__()
    
    
            self.ui = timeSeriesViewer.ui
            self.scrollArea = self.ui.scrollAreaSubsets
    
            self.TSV = timeSeriesViewer
    
            self.targetLayout = self.ui.scrollAreaSubsetContent.layout()
            self.dockMapViews = self.ui.dockMapViews
    
            self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.timeSeriesDateViewCollection = TimeSeriesDateViewCollection(self)
            self.timeSeriesDateViewCollection.sigResizeRequired.connect(self.adjustScrollArea)
    
            self.timeSeriesDateViewCollection.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
            self.timeSeriesDateViewCollection.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
    
            self.TS.sigTimeSeriesDatesAdded.connect(self.timeSeriesDateViewCollection.addDates)
            self.TS.sigTimeSeriesDatesRemoved.connect(self.timeSeriesDateViewCollection.removeDates)
            #add dates, if already existing
            self.timeSeriesDateViewCollection.addDates(self.TS[:])
    
            self.setSpatialExtent(self.TS.getMaxSpatialExtent())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.setSubsetSize(QSize(100,50))
    
    
        def createMapView(self):
            self.MVC.createMapView()
    
            for tsdv in self.timeSeriesDateViewCollection:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setSubsetSize(self, size):
            assert isinstance(size, QSize)
            self.subsetSize = size
    
            self.timeSeriesDateViewCollection.setSubsetSize(size)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.adjustScrollArea()
    
    
            for tsdView in self.timeSeriesDateViewCollection:
                tsdView.redraw()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def adjustScrollArea(self):
    
            #adjust scroll area widget to fit all visible widgets
            m = self.targetLayout.contentsMargins()
            n = len(self.timeSeriesDateViewCollection)
            w = h = 0
    
            s = QSize()
            r = None
            tmp = [v for v in self.timeSeriesDateViewCollection if not v.ui.isVisible()]
            for TSDView in [v for v in self.timeSeriesDateViewCollection if v.ui.isVisible()]:
                s = s + TSDView.ui.sizeHint()
                if r is None:
                    r = TSDView.ui.sizeHint()
            if r:
                if isinstance(self.targetLayout, QHBoxLayout):
    
                    s = QSize(s.width(), r.height())
                else:
                    s = QSize(r.width(), s.height())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                s = s + QSize(m.left() + m.right(), m.top() + m.bottom())
                self.targetLayout.parentWidget().setFixedSize(s)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
        def setMaxTSDViews(self, n=-1):
            self.nMaxTSDViews = n
            #todo: remove views
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.extent = extent
    
            if extent:
    
                self.timeSeriesDateViewCollection.setSpatialExtent(extent)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(TSD, TimeSeriesDatum)
            #get widget related to TSD
    
            tsdv = self.timeSeriesDateViewCollection.tsdView(TSD)
            assert isinstance(self.scrollArea, QScrollArea)
            self.scrollArea.ensureWidgetVisible(tsdv.ui)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def setMapViewVisibility(self, bandView, isVisible):
    
            assert isinstance(bandView, MapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(isVisible, bool)
    
            for tsdv in self.TSDViews:
    
                tsdv.setMapViewVisibility(bandView, isVisible)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    class TimeSeriesDateViewCollection(QObject):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        sigResizeRequired = pyqtSignal()
        sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
        sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
    
        sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, STViz):
            assert isinstance(STViz, SpatialTemporalVisualization)
            super(TimeSeriesDateViewCollection, self).__init__()
            #self.tsv = tsv
            #self.timeSeries = tsv.TS
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.ui = self.STViz.targetLayout.parentWidget()
            self.scrollArea = self.ui.parentWidget().parentWidget()
    
            #potentially there are many more dates than views.
            #therefore we implement the addinng/removing of mapviews here
            #we reduce the number of layout refresh calls by
            #suspending signals, adding the new map view canvases, and sending sigResizeRequired
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.STViz.MVC.sigMapViewAdded.connect(self.addMapView)
            self.STViz.MVC.sigMapViewRemoved.connect(self.removeMapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.setFocusView(None)
            self.setSubsetSize(QSize(50,50))
    
        def tsdView(self, tsd):
            r = [v for v in self.views if v.TSD == tsd]
            if len(r) == 1:
                return r[0]
            else:
                raise Exception('TSD not in list')
    
    
        def addMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            w.setUpdatesEnabled(False)
            for tsdv in self.views:
                tsdv.ui.setUpdatesEnabled(False)
    
            for tsdv in self.views:
                tsdv.insertMapView(mapView)
    
            for tsdv in self.views:
                tsdv.ui.setUpdatesEnabled(True)
    
            #mapView.sigSensorRendererChanged.connect(lambda *args : self.setRasterRenderer(mapView, *args))
            w.setUpdatesEnabled(True)
            self.sigResizeRequired.emit()
    
        def removeMapView(self, mapView):
            assert isinstance(mapView, MapView)
            for tsdv in self.views:
                tsdv.removeMapView(mapView)
            self.sigResizeRequired.emit()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def setFocusView(self, tsd):
            self.focusView = tsd
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def setSpatialExtent(self, extent):
            for tsdview in self.orderedViews():
                tsdview.setSpatialExtent(extent)
    
        def orderedViews(self):
            #returns the
            if self.focusView is not None:
                assert isinstance(self.focusView, TimeSeriesDatumView)
                return sorted(self.views,key=lambda v: np.abs(v.TSD.date - self.focusView.TSD.date))
            else:
                return self.views
    
        def setSubsetSize(self, size):
            assert isinstance(size, QSize)
            self.subsetSize = size
    
    
            for tsdView in self.orderedViews():
                tsdView.blockSignals(True)
    
    
            for tsdView in self.orderedViews():
                tsdView.setSubsetSize(size)
    
            for tsdView in self.orderedViews():
                tsdView.blockSignals(False)
    
    
        def addDates(self, tsdList):
            """
            Create a new TSDView
            :param tsdList:
            :return:
            """
            for tsd in tsdList:
                assert isinstance(tsd, TimeSeriesDatum)
    
                tsdView = TimeSeriesDatumView(tsd, self, self.STViz.MVC, parent=self.ui)
    
                tsdView.setSubsetSize(self.subsetSize)
    
                tsdView.sigExtentsChanged.connect(self.setSpatialExtent)
                tsdView.sigLoadingStarted.connect(self.sigLoadingStarted.emit)
                tsdView.sigLoadingFinished.connect(self.sigLoadingFinished.emit)
                tsdView.sigVisibilityChanged.connect(lambda: self.STViz.adjustScrollArea())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
                for i, mapView in enumerate(self.STViz.MVC):
                    tsdView.insertMapView(mapView)
    
                bisect.insort(self.views, tsdView)
                tsdView.ui.setParent(self.STViz.targetLayout.parentWidget())
                self.STViz.targetLayout.addWidget(tsdView.ui)
                tsdView.ui.show()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if len(tsdList) > 0:
                self.sigResizeRequired.emit()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def removeDates(self, tsdList):
            toRemove = [v for v in self.views if v.TSD in tsdList]
            removedDates = []
            for tsdView in toRemove:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                tsdView.ui.parent().layout().removeWidget(tsdView.ui)
                tsdView.ui.hide()
                tsdView.ui.close()
                removedDates.append(tsdView.TSD)
                del tsdView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if len(removedDates) > 0:
                self.sigResizeRequired.emit()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __len__(self):
            return len(self.views)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __iter__(self):
            return iter(self.views)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __getitem__(self, slice):
            return self.views[slice]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __delitem__(self, slice):
            self.removeDates(self.views[slice])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        sigMapViewAdded = pyqtSignal(MapView)
        sigMapViewRemoved = pyqtSignal(MapView)
        sigSetMapViewVisibility = pyqtSignal(MapView, bool)
    
        sigShowProfiles = pyqtSignal(QgsPoint, QgsCoordinateReferenceSystem)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, STViz):
            assert isinstance(STViz, SpatialTemporalVisualization)
            super(MapViewCollection, self).__init__()
            self.STViz = STViz
            self.STViz.dockMapViews.actionApplyStyles.triggered.connect(self.applyStyles)
            self.STViz.TS.sigSensorAdded.connect(self.addSensor)
    
            self.ui = STViz.dockMapViews
    
            self.btnList = STViz.dockMapViews.BVButtonList
            self.scrollArea = STViz.dockMapViews.scrollAreaMapViews
            self.scrollAreaContent = STViz.dockMapViews.scrollAreaMapsViewDockContent
    
            self.mapViewsDefinitions = []
            self.mapViewButtons = dict()
    
            self.adjustScrollArea()
        def applyStyles(self):
            for mapView in self.mapViewsDefinitions:
                mapView.applyStyles()
    
        def index(self, mapView):
            assert isinstance(mapView, MapView)
            return self.mapViewsDefinitions.index(mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def adjustScrollArea(self):
            #adjust scroll area widget to fit all visible widgets
            l = self.scrollAreaContent.layout()
            from timeseriesviewer.ui.widgets import maxWidgetSizes
            newSize = maxWidgetSizes(l)
            #print(newSize)
            #newSize = self.scrollAreaContent.sizeHint()
            self.scrollAreaContent.setFixedSize(newSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def addSensor(self, sensor):
            for mapView in self.mapViewsDefinitions:
                mapView.addSensor(sensor)
            self.adjustScrollArea()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def removeSensor(self, sensor):
            for mapView in self.mapViewsDefinitions:
                mapView.removeSensor(sensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def createMapView(self):
    
            btn = QToolButton(self.btnList)
            self.btnList.layout().insertWidget(self.btnList.layout().count() - 1, btn)
    
            mapView = MapView(self, parent=self.scrollArea)
    
            mapView.sigRemoveMapView.connect(self.removeMapView)
    
            mapView.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            for sensor in self.STViz.TS.Sensors:
                mapView.addSensor(sensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.mapViewButtons[mapView] = btn
            self.mapViewsDefinitions.append(mapView)
    
    
            btn.clicked.connect(lambda : self.showMapViewDefinition(mapView))
            self.refreshMapViewTitles()
            if len(self) == 1:
                self.showMapViewDefinition(mapView)
    
            self.sigMapViewAdded.emit(mapView)
            self.adjustScrollArea()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def removeMapView(self, mapView):
    
            assert isinstance(mapView, MapView)
    
            btn = self.mapViewButtons[mapView]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            idx = self.mapViewsDefinitions.index(mapView)
    
            self.mapViewsDefinitions.remove(mapView)
            self.mapViewButtons.pop(mapView)
    
            mapView.ui.setVisible(False)
    
            self.btnList.layout().removeWidget(btn)
            l = self.scrollAreaContent.layout()
    
    
            for d in self.recentMapViewDefinitions():
                d.ui.setVisible(False)
                l.removeWidget(d.ui)
            l.removeWidget(mapView.ui)
            mapView.ui.close()
    
            self.refreshMapViewTitles()
            self.sigMapViewRemoved.emit(mapView)
            if len(self) > 0:
                #show previous mapViewDefinition
                idxNext = max([idx-1, 0])
                self.showMapViewDefinition(self.mapViewsDefinitions[idxNext])
    
        def refreshMapViewTitles(self):
    
            for i, mapView in enumerate(self.mapViewsDefinitions):
                number = i+1
                title = '#{}'.format(number)
                mapView.setTitle(title)
                btn = self.mapViewButtons[mapView]
                btn.setText('{}'.format(number))
                btn.setToolTip('Show definition for map view {}'.format(number))
                btn.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
    
    
    
        def setSpatialExtent(self, extent):
            for mv in self.mapViewsDefinitions:
                mv.setSpatialExtent(extent)
    
        def showMapViewDefinition(self, mapViewDefinition):
            assert mapViewDefinition in self.mapViewsDefinitions
    
            assert isinstance(mapViewDefinition, MapView)
            l = self.scrollAreaContent.layout()
    
            for d in self.recentMapViewDefinitions():
                d.ui.setVisible(False)
                l.removeWidget(d.ui)
    
            l.insertWidget(l.count() - 1, mapViewDefinition.ui)
            mapViewDefinition.ui.setVisible(True)
    
            self.ui.setWindowTitle(self.ui.baseTitle + '|'+mapViewDefinition.title())
    
    
        def recentMapViewDefinitions(self):
    
            from timeseriesviewer.ui.widgets import MapViewDefinitionUI
    
            return [ui.mapViewDefinition() for ui in parent.findChildren(MapViewDefinitionUI)]
    
    
        def setMapViewVisibility(self, bandView, isVisible):
    
            assert isinstance(bandView, MapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
        def __len__(self):
    
            return len(self.mapViewsDefinitions)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def __iter__(self):
    
            return iter(self.mapViewsDefinitions)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def __getitem__(self, key):
    
            return self.mapViewsDefinitions[key]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __contains__(self, mapView):
            return mapView in self.mapViewsDefinitions
    
    class TimeSeriesViewer:
    
    unknown's avatar
    unknown committed
    
        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
    
            from timeseriesviewer.ui.widgets import TimeSeriesViewerUI
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.ui = TimeSeriesViewerUI()
    
                timeseriesviewer.QGIS_TSV_BRIDGE = QgisTsvBridge(iface, self.ui)
    
    
            #init empty time series
            self.TS = TimeSeries()
            self.hasInitialCenterPoint = False
    
            self.TS.sigTimeSeriesDatesAdded.connect(self.datesAdded)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            #self.ICP = D.scrollAreaSubsetContent.layout()
            #D.scrollAreaMapViews.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
            #self.BVP = self.ui.scrollAreaMapViews.layout()
    
            D.dockNavigation.connectTimeSeries(self.TS)
            D.dockTimeSeries.connectTimeSeries(self.TS)
    
            D.dockSensors.connectTimeSeries(self.TS)
    
            D.dockProfiles.connectTimeSeries(self.TS)
    
            self.spectralTemporalVis = D.dockProfiles
    
    
            self.spatialTemporalVis = SpatialTemporalVisualization(self)
            self.spatialTemporalVis.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
            self.spatialTemporalVis.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
    
            self.spatialTemporalVis.sigShowProfiles.connect(self.spectralTemporalVis.loadCoordinate)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.spectralTemporalVis.sigMoveToTSD.connect(self.spatialTemporalVis.navigateToTSD)
            D.dockNavigation.sigSpatialExtentChanged.connect(self.spatialTemporalVis.setSpatialExtent)
    
    unknown's avatar
    unknown committed
            self.ValidatorPxX = QIntValidator(0,99999)
            self.ValidatorPxY = QIntValidator(0,99999)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            #connect actions with logic
    
            #D.btn_showPxCoordinate.clicked.connect(lambda: self.showSubsetsStart())
    
            #connect actions with logic
    
            D.actionSelectCenter.triggered.connect(lambda : self.spatialTemporalVis.activateMapTool('selectCenter'))
    
            #D.actionSelectArea.triggered.connect(lambda : self.spatialTemporalVis.activateMapTool('selectArea'))
    
            D.actionZoomMaxExtent.triggered.connect(lambda : self.zoomTo('maxExtent'))
            D.actionZoomPixelScale.triggered.connect(lambda: self.zoomTo('pixelScale'))
    
            D.actionZoomIn.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('zoomIn'))
            D.actionZoomOut.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('zoomOut'))
            D.actionPan.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('pan'))
    
            D.actionIdentifyTimeSeries.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('identifyProfile'))
            D.actionIdentifyMapLayers.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('identifyMapLayers'))
    
            D.actionAddMapView.triggered.connect(self.spatialTemporalVis.createMapView)
    
            D.actionAddTSD.triggered.connect(lambda : self.ua_addTSImages())
            D.actionRemoveTSD.triggered.connect(lambda: self.TS.removeDates(self.ui.dockTimeSeries.selectedTimeSeriesDates()))
            D.actionRedraw.triggered.connect(self.spatialTemporalVis.redraw)
    
            D.actionLoadTS.triggered.connect(self.loadTimeSeries)
    
            D.actionClearTS.triggered.connect(self.clearTimeSeries)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            D.actionSaveTS.triggered.connect(self.ua_saveTSFile)
            D.actionAddTSExample.triggered.connect(self.ua_loadExampleTS)
    
    
            #connect buttons with actions
    
            from timeseriesviewer.ui.widgets import AboutDialogUI, PropertyDialogUI
    
            D.actionAbout.triggered.connect(lambda: AboutDialogUI(self.ui).exec_())
            D.actionSettings.triggered.connect(lambda : PropertyDialogUI(self.ui).exec_())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            D.actionFirstTSD.triggered.connect(lambda: self.setDOISliderValue('first'))
            D.actionLastTSD.triggered.connect(lambda: self.setDOISliderValue('last'))
            D.actionNextTSD.triggered.connect(lambda: self.setDOISliderValue('next'))
            D.actionPreviousTSD.triggered.connect(lambda: self.setDOISliderValue('previous'))
    
    
    
            D.dockRendering.actionSetSubsetSize.triggered.connect(lambda : self.spatialTemporalVis.setSubsetSize(
                                                    D.dockRendering.subsetSize()))
            D.actionSetExtent.triggered.connect(lambda: self.spatialTemporalVis.setSpatialExtent(self.ui.spatialExtent()))
    
            self.canvasCrs = QgsCoordinateReferenceSystem()
    
        def loadImageFiles(self, files):
            assert isinstance(files, list)
            self.TS.addFiles(files)