Skip to content
Snippets Groups Projects
mapvisualization.py 62.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • # -*- coding: utf-8 -*-
    """
    /***************************************************************************
                                  HUB TimeSeriesViewer
                                  -------------------
            begin                : 2015-08-20
            git sha              : $Format:%H$
            copyright            : (C) 2017 by HU-Berlin
            email                : benjamin.jakimow@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.                                   *
     *                                                                         *
     ***************************************************************************/
    """
    # noinspection PyPep8Naming
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from __future__ import absolute_import, unicode_literals
    
    import os, sys, re, fnmatch, collections, copy, traceback, six, bisect
    import logging
    logger = logging.getLogger(__name__)
    from qgis.core import *
    
    from PyQt4.QtXml import *
    from PyQt4.QtCore import *
    from PyQt4.QtGui import *
    
    import numpy as np
    from timeseriesviewer.utils import *
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from timeseriesviewer.timeseries import SensorInstrument, TimeSeriesDatum, TimeSeries
    
    from timeseriesviewer.ui.docks import TsvDockWidgetBase, loadUI
    
    from timeseriesviewer.main import TsvMimeDataUtils
    
    from timeseriesviewer.ui.mapviewscrollarea import MapViewScrollArea
    
    from timeseriesviewer.mapcanvas import MapCanvas
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from timeseriesviewer.crosshair import CrosshairStyle
    
    
    class MapViewUI(QFrame, loadUI('mapviewdefinition.ui')):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __init__(self, parent=None):
            super(MapViewUI, self).__init__(parent)
            self.setupUi(self)
            self.mSensors = collections.OrderedDict()
    
            m = QMenu(self.btnToggleCrosshair)
            m.addAction(self.actionSetCrosshairStyle)
            #a = m.addAction('Set Crosshair Style')
    
            self.btnToggleCrosshair.setMenu(m)
    
    
            #connect the QActions with the QgsCollapsibleGroupBoxes
            self.gbVectorRendering.toggled.connect(self.actionToggleVectorVisibility.toggle)
            self.gbRasterRendering.toggled.connect(self.actionToggleRasterVisibility.toggle)
    
            self.actionToggleVectorVisibility.toggled.connect(self.gbVectorRendering.setChecked)
            self.actionToggleRasterVisibility.toggled.connect(self.gbRasterRendering.setChecked)
            #self.actionToggleRasterVisibility.toggled.connect(self.dummy)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.btnToggleCrosshair.setDefaultAction(self.actionToggleCrosshairVisibility)
    
            self.btnToggleMapViewVisibility.setDefaultAction(self.actionToggleMapViewVisibility)
    
        def dummy(self, *args):
            print((self.sender(), args))
            print(args)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def addSensor(self, sensor):
            assert isinstance(sensor, SensorInstrument)
    
            w = MapViewSensorSettings(sensor)
    
            #sizePolicy = QSizePolicy(QSize)
            #w.ui.
            l = self.renderSettingsLayout
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert sensor not in self.mSensors.keys()
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            l.insertWidget(lastWidgetIndex, w.ui)
            self.mSensors[sensor] = w
            #self.resize(self.sizeHint())
    
            return w
    
    
        def removeSensor(self, sensor):
    
            assert isinstance(sensor, SensorInstrument)
            sensorSettings = self.mSensors.pop(sensor)
            assert isinstance(sensorSettings, MapViewSensorSettings)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            l.removeWidget(sensorSettings.ui)
            sensorSettings.ui.close()
            #self.resize(self.sizeHint())
    
    class MapView(QObject):
    
        sigRemoveMapView = pyqtSignal(object)
        sigMapViewVisibility = pyqtSignal(bool)
    
        #sigVectorVisibility = pyqtSignal(bool)
        #sigRasterVisibility = pyqtSignal(bool)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        sigTitleChanged = pyqtSignal([str],[unicode])
    
        sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
        sigShowCrosshair = pyqtSignal(bool)
        sigVectorLayerChanged = pyqtSignal()
    
    
        sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, mapViewCollection, name='Map View', recommended_bands=None, parent=None):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(mapViewCollection, MapViewCollectionDock)
    
            self.ui = MapViewUI(mapViewCollection.stackedWidget)
            self.ui.show()
            self.ui.cbQgsVectorLayer.setFilters(QgsMapLayerProxyModel.VectorLayer)
    
            self.ui.cbQgsVectorLayer.layerChanged.connect(self.setVectorLayer)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.tbName.textChanged.connect(self.sigTitleChanged)
            from timeseriesviewer.crosshair import getCrosshairStyle
            self.ui.actionSetCrosshairStyle.triggered.connect(
                lambda : self.setCrosshairStyle(getCrosshairStyle(
                    parent=self.ui,
                    crosshairStyle=self.mCrosshairStyle))
            )
    
    
            self.mapViewCollection = mapViewCollection
    
            self.sensorViews = collections.OrderedDict()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mCrosshairStyle = CrosshairStyle()
            self.mShowCrosshair = True
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mIsVisible = True
    
            self.mVectorsVisible = True
            self.mRastersVisible = True
    
            self.ui.actionToggleVectorVisibility.setChecked(True)
            self.ui.actionToggleVectorVisibility.toggled.connect(self.setVectorVisibility)
    
            self.ui.actionToggleRasterVisibility.setChecked(True)
            self.ui.actionToggleRasterVisibility.toggled.connect(self.setRasterVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.actionToggleCrosshairVisibility.toggled.connect(self.setShowCrosshair)
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            self.ui.actionToggleMapViewVisibility.toggled.connect(lambda b: self.setIsVisible(not b))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
    
            self.ui.actionToggleVectorVisibility.setChecked(True)
            self.ui.actionToggleRasterVisibility.setChecked(True)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.setTitle(name)
            #forward actions with reference to this band view
    
        def dummy(self, *args):
            print(args)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setIsVisible(self, b):
            assert isinstance(b, bool)
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            changed = b != self.mIsVisible
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mIsVisible = b
    
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setVisible(b)
            if changed:
                self.sigMapViewVisibility.emit(b)
    
    
        def isVisible(self):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.mIsVisible
    
    
        def mapCanvases(self):
            m = []
            for sensor, sensorView in self.sensorViews.items():
                m.extend(sensorView.mapCanvases())
            return m
    
        def vectorLayerRenderer(self):
            if isinstance(self.mVectorLayer, QgsVectorLayer):
                return self.mVectorLayer.rendererV2()
            return None
    
        def setVectorLayerRenderer(self, renderer):
            if isinstance(renderer, QgsFeatureRendererV2) and \
                isinstance(self.mVectorLayer, QgsVectorLayer):
                self.mVectorLayer.setRendererV2(renderer)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                #add vector layer
                self.mVectorLayer = lyr
                self.mVectorLayer.rendererChanged.connect(self.sigVectorLayerChanged)
    
                for mapCanvas in self.mapCanvases():
                    assert isinstance(mapCanvas, MapCanvas)
    
                    mapCanvas.layerModel().setVectorLayerSources([self.mVectorLayer])
                    #mapCanvas.setLayers([l for l in mapCanvas.layers() if isinstance(l, QgsRasterLayer)])
                    #mapCanvas.setLazyVectorSources([lyr])
    
                #remove vector layers
                self.mVectorLayer = None
                for mapCanvas in self.mapCanvases():
    
                    mapCanvas.layerModel().setVectorLayerSources([])
                    #mapCanvas.setLayers([l for l in mapCanvas.mLayers if not isinstance(l, QgsVectorLayer)])
                    mapCanvas.refresh()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.sigVectorLayerChanged.emit()
    
        def applyStyles(self):
            for sensorView in self.sensorViews.values():
                sensorView.applyStyle()
    
        def setTitle(self, title):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            old = self.title()
            if old != title:
                self.ui.tbName.setText(title)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.ui.tbName.text()
    
        def refreshMapView(self, *args):
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.refresh()
    
    
            if isinstance(crosshairStyle, CrosshairStyle):
                old = self.mCrosshairStyle
                self.mCrosshairStyle = crosshairStyle
                if old != self.mCrosshairStyle:
                    self.sigCrosshairStyleChanged.emit(self.mCrosshairStyle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def setHighlighted(self, b=True, timeout=1000):
            styleOn = """.MapCanvas {
                        border: 4px solid red;
                        border-radius: 4px;
                    }"""
            styleOff = """"""
            if b is True:
                for mapCanvas in self.mapCanvases():
                    mapCanvas.setStyleSheet(styleOn)
                if timeout > 0:
                    QTimer.singleShot(timeout, lambda : self.setHighlighted(False))
            else:
                for mapCanvas in self.mapCanvases():
                    mapCanvas.setStyleSheet(styleOff)
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(b, bool)
            self.mShowCrosshair = b
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def showCrosshair(self):
            return self.mShowCrosshair and self.mCrosshairStyle is not None
    
    
    
        def rasterVisibility(self):
            return self.mRastersVisible
    
        def vectorVisibility(self):
            return self.mVectorsVisible
    
        def setRasterVisibility(self, b):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(b, bool)
    
            if self.rasterVisibility() != b:
                self.mRastersVisible = b
                self.ui.actionToggleRasterVisibility.setChecked(b)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                for mapCanvas in self.mapCanvases():
                    assert isinstance(mapCanvas, MapCanvas)
                    mapCanvas.layerModel().setRasterLayerVisibility(b)
                    mapCanvas.refresh()
    
                #self.sigRasterVisibility.emit(b)
    
        def setVectorVisibility(self, b):
            assert isinstance(b, bool)
            if self.vectorVisibility() != b:
                self.mVectorsVisible = b
                self.ui.actionToggleVectorVisibility.setChecked(b)
    
                for mapCanvas in self.mapCanvases():
                    assert isinstance(mapCanvas, MapCanvas)
                    mapCanvas.layerModel().setVectorLayerVisibility(b)
                    mapCanvas.refresh()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert sensor in self.sensorViews.keys()
            self.sensorViews.pop(sensor)
            self.ui.removeSensor(sensor)
            return True
    
    
        def hasSensor(self, sensor):
            assert type(sensor) is SensorInstrument
            return sensor in self.sensorViews.keys()
    
    
        def registerMapCanvas(self, sensor, mapCanvas):
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
            assert isinstance(sensor, SensorInstrument)
    
            sensorView = self.sensorViews[sensor]
    
            assert isinstance(sensorView, MapViewSensorSettings)
            sensorView.registerMapCanvas(mapCanvas)
    
            #register signals sensor specific signals
    
            mapCanvas.setRenderer(sensorView.rasterLayerRenderer())
    
            mapCanvas.setRenderer(self.vectorLayerRenderer())
    
            #register non-sensor specific signals for this mpa view
    
            self.sigMapViewVisibility.connect(mapCanvas.refresh)
            self.sigCrosshairStyleChanged.connect(mapCanvas.setCrosshairStyle)
            self.sigShowCrosshair.connect(mapCanvas.setShowCrosshair)
            self.sigVectorLayerChanged.connect(mapCanvas.refresh)
    
    #        self.sigVectorVisibility.connect(mapCanvas.refresh)
    
        def addSensor(self, sensor):
            """
            :param sensor:
            :return:
            """
            assert type(sensor) is SensorInstrument
            assert sensor not in self.sensorViews.keys()
    
            w = self.ui.addSensor(sensor)
    
            #w.sigSensorRendererChanged.connect(self.onSensorRenderingChanged)
    
            self.sensorViews[sensor] = w
            s  =""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onSensorRenderingChanged(self, renderer):
            sensorSettings = self.sender()
            assert isinstance(sensorSettings, MapViewSensorSettings)
            for mapCanvas in sensorSettings.mapCanvases():
                mapCanvas.setRenderer(renderer)
                #mapCanvas.refresh()
    
        def getSensorWidget(self, sensor):
            assert type(sensor) is SensorInstrument
            return self.sensorViews[sensor]
    
    
    
    
    class MapViewRenderSettingsUI(QgsCollapsibleGroupBox, loadUI('mapviewrendersettings.ui')):
    
    
        def __init__(self, parent=None):
            """Constructor."""
            super(MapViewRenderSettingsUI, self).__init__(parent)
            # Set up the user interface from Designer.
            # After setupUI you can access any designer object by doing
            # self.<objectname>, and you can use autoconnect slots - see
            # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
            # #widgets-and-dialogs-with-auto-connect
    
            self.setupUi(self)
    
            self.btnDefaultMB.setDefaultAction(self.actionSetDefaultMB)
            self.btnTrueColor.setDefaultAction(self.actionSetTrueColor)
            self.btnCIR.setDefaultAction(self.actionSetCIR)
            self.btn453.setDefaultAction(self.actionSet453)
    
            self.btnSingleBandDef.setDefaultAction(self.actionSetDefaultSB)
            self.btnSingleBandBlue.setDefaultAction(self.actionSetB)
            self.btnSingleBandGreen.setDefaultAction(self.actionSetG)
            self.btnSingleBandRed.setDefaultAction(self.actionSetR)
            self.btnSingleBandNIR.setDefaultAction(self.actionSetNIR)
            self.btnSingleBandSWIR.setDefaultAction(self.actionSetSWIR)
    
            self.btnPasteStyle.setDefaultAction(self.actionPasteStyle)
            self.btnCopyStyle.setDefaultAction(self.actionCopyStyle)
            self.btnApplyStyle.setDefaultAction(self.actionApplyStyle)
    
    
    
    class MapViewSensorSettings(QObject):
        """
        Describes the rendering of images of one Sensor
        """
    
    
        sigSensorRendererChanged = pyqtSignal(QgsRasterRenderer)
    
    
        def __init__(self, sensor, parent=None):
            """Constructor."""
            super(MapViewSensorSettings, self).__init__(parent)
            from timeseriesviewer.timeseries import SensorInstrument
            assert isinstance(sensor, SensorInstrument)
            self.sensor = sensor
    
            self.ui = MapViewRenderSettingsUI(parent)
            self.ui.create()
    
            self.ui.stackedWidget.currentChanged.connect(self.updateUi)
    
            self.sensor.sigNameChanged.connect(self.onSensorNameChanged)
            self.onSensorNameChanged(self.sensor.name())
    
            self.ui.bandNames = sensor.bandNames
    
            self.multiBandMinValues = [self.ui.tbRedMin, self.ui.tbGreenMin, self.ui.tbBlueMin]
            self.multiBandMaxValues = [self.ui.tbRedMax, self.ui.tbGreenMax, self.ui.tbBlueMax]
            self.multiBandSliders = [self.ui.sliderRed, self.ui.sliderGreen, self.ui.sliderBlue]
    
    
            for tb in self.multiBandMinValues + self.multiBandMaxValues + [self.ui.tbSingleBandMin, self.ui.tbSingleBandMax]:
    
                tb.textChanged.connect(self.onValuesChanged)
    
    
            for sl in self.multiBandSliders + [self.ui.sliderSingleBand]:
                sl.setMinimum(1)
                sl.setMaximum(sensor.nb)
                sl.valueChanged.connect(self.updateUi)
    
    
            self.ceAlgs = collections.OrderedDict()
    
            self.ceAlgs["No enhancement"] = QgsContrastEnhancement.NoEnhancement
            self.ceAlgs["Stretch to MinMax"] = QgsContrastEnhancement.StretchToMinimumMaximum
            self.ceAlgs["Stretch and clip to MinMax"] = QgsContrastEnhancement.StretchAndClipToMinimumMaximum
            self.ceAlgs["Clip to MinMax"] = QgsContrastEnhancement.ClipToMinimumMaximum
    
            self.colorRampType = collections.OrderedDict()
            self.colorRampType['Interpolated'] = QgsColorRampShader.INTERPOLATED
            self.colorRampType['Discrete'] = QgsColorRampShader.DISCRETE
            self.colorRampType['Exact'] = QgsColorRampShader.EXACT
    
            self.colorRampClassificationMode = collections.OrderedDict()
            self.colorRampClassificationMode['Continuous'] = 1
            self.colorRampClassificationMode['Equal Interval'] = 2
            self.colorRampClassificationMode['Quantile'] = 3
    
            def populateCombobox(cb, d):
                for key, value in d.items():
                    cb.addItem(key, value)
                cb.setCurrentIndex(0)
    
            populateCombobox(self.ui.comboBoxContrastEnhancement, self.ceAlgs)
            populateCombobox(self.ui.cbSingleBandColorRampType, self.colorRampType)
            populateCombobox(self.ui.cbSingleBandMode, self.colorRampClassificationMode)
    
            self.ui.cbSingleBandColorRamp.populate(QgsStyleV2.defaultStyle())
    
    
            nb = self.sensor.nb
            lyr = QgsRasterLayer(self.sensor.pathImg)
    
            #define default renderers:
            bands = [min([b,nb-1]) for b in range(3)]
            extent = lyr.extent()
    
            bandStats = [lyr.dataProvider().bandStatistics(b, QgsRasterBandStats.All, extent, 500) for b in range(nb)]
    
            def createEnhancement(bandIndex):
                bandIndex = min([nb - 1, bandIndex])
                e = QgsContrastEnhancement(self.sensor.bandDataType)
                e.setMinimumValue(bandStats[bandIndex].Min)
                e.setMaximumValue(bandStats[bandIndex].Max)
                e.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
                return e
    
            self.defaultMB = QgsMultiBandColorRenderer(lyr.dataProvider(), bands[0], bands[1], bands[2])
            self.defaultMB.setRedContrastEnhancement(createEnhancement(bands[0]))
            self.defaultMB.setGreenContrastEnhancement(createEnhancement(bands[1]))
            self.defaultMB.setBlueContrastEnhancement(createEnhancement(bands[2]))
    
            self.defaultSB = QgsSingleBandPseudoColorRenderer(lyr.dataProvider(), 0, None)
    
            colorRamp = self.ui.cbSingleBandColorRamp.currentColorRamp()
    
            #fix: QGIS 3.0 constructor
            shaderFunc = QgsColorRampShader(bandStats[0].Min, bandStats[0].Max)
            shaderFunc.setColorRampType(QgsColorRampShader.INTERPOLATED)
            shaderFunc.setClip(True)
            nSteps = 5
            colorRampItems = []
            diff = bandStats[0].Max - bandStats[0].Min
            for  i in range(nSteps+1):
                f = float(i) / nSteps
                color = colorRamp.color(f)
                value = bandStats[0].Min + diff * f
                colorRampItems.append(QgsColorRampShader.ColorRampItem(value, color))
            shaderFunc.setColorRampItemList(colorRampItems)
            shader = QgsRasterShader()
    
            shader.setMaximumValue(bandStats[0].Max)
            shader.setMinimumValue(bandStats[0].Min)
    
            shader.setRasterShaderFunction(shaderFunc)
            self.defaultSB.setShader(shader)
            self.defaultSB.setClassificationMin(shader.minimumValue())
            self.defaultSB.setClassificationMax(shader.maximumValue())
            #init connect signals
            self.ui.actionSetDefaultMB.triggered.connect(lambda : self.setBandSelection('defaultMB'))
            self.ui.actionSetTrueColor.triggered.connect(lambda: self.setBandSelection('TrueColor'))
            self.ui.actionSetCIR.triggered.connect(lambda: self.setBandSelection('CIR'))
            self.ui.actionSet453.triggered.connect(lambda: self.setBandSelection('453'))
    
            self.ui.actionSetDefaultSB.triggered.connect(lambda: self.setBandSelection('defaultSB'))
            self.ui.actionSetB.triggered.connect(lambda: self.setBandSelection('B'))
            self.ui.actionSetG.triggered.connect(lambda: self.setBandSelection('G'))
            self.ui.actionSetR.triggered.connect(lambda: self.setBandSelection('R'))
            self.ui.actionSetNIR.triggered.connect(lambda: self.setBandSelection('nIR'))
            self.ui.actionSetSWIR.triggered.connect(lambda: self.setBandSelection('swIR'))
    
    
            self.ui.actionApplyStyle.triggered.connect(self.applyStyle)
    
            self.ui.actionCopyStyle.triggered.connect(lambda : QApplication.clipboard().setMimeData(self.mimeDataStyle()))
            self.ui.actionPasteStyle.triggered.connect(lambda : self.pasteStyleFromClipboard())
    
            #self.ui.stackedWidget
    
            if not self.sensor.wavelengthsDefined():
                self.ui.btnTrueColor.setEnabled(False)
                self.ui.btnCIR.setEnabled(False)
                self.ui.btn453.setEnabled(False)
    
                self.ui.btnSingleBandBlue.setEnabled(False)
                self.ui.btnSingleBandGreen.setEnabled(False)
                self.ui.btnSingleBandRed.setEnabled(False)
                self.ui.btnSingleBandNIR.setEnabled(False)
                self.ui.btnSingleBandSWIR.setEnabled(False)
    
            #apply recent or default renderer
            renderer = lyr.renderer()
    
            #set defaults
            self.setLayerRenderer(self.defaultSB)
            self.setLayerRenderer(self.defaultMB)
    
    
            if type(renderer) in [QgsMultiBandColorRenderer, QgsSingleBandPseudoColorRenderer]:
                self.setLayerRenderer(renderer)
    
    
            QApplication.clipboard().dataChanged.connect(self.onClipboardChange)
            self.onClipboardChange()
    
    
        def mapCanvases(self):
            return self.mMapCanvases[:]
    
    
        def registerMapCanvas(self, mapCanvas):
    
            assert isinstance(mapCanvas, MapCanvas)
            self.mMapCanvases.append(mapCanvas)
            mapCanvas.sigChangeSVRequest.connect(self.onMapCanvasRendererChangeRequest)
    
    
    
            self.ui.actionApplyStyle.setToolTip('Apply style to all map view images from "{}"'.format(self.sensor.name()))
    
        def pasteStyleFromClipboard(self):
            utils = TsvMimeDataUtils(QApplication.clipboard().mimeData())
            if utils.hasRasterStyle():
                renderer = utils.rasterStyle(self.sensor.bandDataType)
                if renderer is not None:
                    self.setLayerRenderer(renderer)
    
    
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
    
                mapCanvas.layerModel().setRenderer(r)
                mapCanvas.refresh()
    
    
        def onClipboardChange(self):
            utils = TsvMimeDataUtils(QApplication.clipboard().mimeData())
            self.ui.btnPasteStyle.setEnabled(utils.hasRasterStyle())
    
    
        def onMapCanvasRendererChangeRequest(self, mapCanvas, renderer):
            self.setLayerRenderer(renderer)
    
    
        def setBandSelection(self, key):
    
    
            if key == 'defaultMB':
                bands = [self.defaultMB.redBand(), self.defaultMB.greenBand(), self.defaultMB.blueBand()]
            elif key == 'defaultSB':
                bands = [self.defaultSB.band()]
    
            else:
                if key in ['R','G','B','nIR','swIR']:
                    colors = [key]
                elif key == 'TrueColor':
                    colors = ['R','G','B']
                elif key == 'CIR':
                    colors = ['nIR', 'R', 'G']
                elif key == '453':
                    colors = ['nIR','swIR', 'R']
                bands = [self.sensor.bandClosestToWavelength(c) for c in colors]
    
            if len(bands) == 1:
                self.ui.sliderSingleBand.setValue(bands[0]+1)
            elif len(bands) == 3:
                for i, b in enumerate(bands):
                    self.multiBandSliders[i].setValue(b+1)
    
    
    
        def onValuesChanged(self, text):
            styleValid = ""
            styleInValid = """.QLineEdit {border-color:red;
                               border-style: outset;
                               border-width: 2px;
                               background-color: yellow }
                            """
            w = self.sender()
            if isinstance(w, QLineEdit):
                validator = w.validator()
                assert isinstance(validator, QDoubleValidator)
                res = validator.validate(text, 0)
                if res[0] == QDoubleValidator.Acceptable:
                    w.setStyleSheet(styleValid)
                else:
                    w.setStyleSheet(styleInValid)
    
    
            cw = self.ui.stackedWidget.currentWidget()
            text = ''
            if cw == self.ui.pageMultiBand:
    
                    self.ui.sliderRed.value(),
                    self.ui.sliderGreen.value(),
                    self.ui.sliderBlue.value()
                )
            elif cw == self.ui.pageSingleBand:
    
                text = 'Singleband({})'.format(self.ui.sliderSingleBand.value())
    
            text = '{} - {}'.format(self.sensor.name(), text)
            self.ui.setTitle(text)
    
    
    
        def setLayerRenderer(self, renderer):
            ui = self.ui
            assert isinstance(renderer, QgsRasterRenderer)
    
            from timeseriesviewer.utils import niceNumberString
    
            updated = False
            if isinstance(renderer, QgsMultiBandColorRenderer):
                self.ui.cbRenderType.setCurrentIndex(0)
                #self.ui.stackedWidget.setcurrentWidget(self.ui.pageMultiBand)
    
                for s in self.multiBandSliders:
                    s.blockSignals(True)
                ui.sliderRed.setValue(renderer.redBand())
                ui.sliderGreen.setValue(renderer.greenBand())
                ui.sliderBlue.setValue(renderer.blueBand())
                for s in self.multiBandSliders:
                    s.blockSignals(False)
    
    
                ceRed = renderer.redContrastEnhancement()
                ceGreen = renderer.greenContrastEnhancement()
                ceBlue = renderer.blueContrastEnhancement()
    
    
                if ceRed is None:
                    ceRed = ceGreen = ceBlue = QgsContrastEnhancement(self.sensor.bandDataType)
                    s = ""
    
                for i, ce in enumerate([ceRed, ceGreen, ceBlue]):
    
                    vMin = ce.minimumValue()
                    vMax = ce.maximumValue()
    
                    self.multiBandMinValues[i].setText(niceNumberString(vMin))
                    self.multiBandMaxValues[i].setText(niceNumberString(vMax))
    
    
                idx = self.ceAlgs.values().index(ceRed.contrastEnhancementAlgorithm())
                ui.comboBoxContrastEnhancement.setCurrentIndex(idx)
    
                updated = True
    
            if isinstance(renderer, QgsSingleBandPseudoColorRenderer):
                self.ui.cbRenderType.setCurrentIndex(1)
                #self.ui.stackedWidget.setCurrentWidget(self.ui.pageSingleBand)
    
                self.ui.sliderSingleBand.setValue(renderer.band())
                shader = renderer.shader()
                cmin = shader.minimumValue()
                cmax = shader.maximumValue()
                self.ui.tbSingleBandMin.setText(str(cmin))
                self.ui.tbSingleBandMax.setText(str(cmax))
    
                shaderFunc = shader.rasterShaderFunction()
                self.ui.cbSingleBandColorRampType.setCurrentIndex(shaderFunc.colorRampType())
                updated = True
    
            self.updateUi()
    
            if updated:
                self.mLastRenderer = self.rasterLayerRenderer()
    
            if updated and MapViewSensorSettings.SignalizeImmediately:
    
                self.sigSensorRendererChanged.emit(renderer.clone())
    
            if isinstance(r, QgsRasterRenderer):
                doc = QDomDocument()
                lyr = QgsRasterLayer(self.sensor.pathImg)
                lyr.setRenderer(self.rasterLayerRenderer())
                err = ''
                lyr.exportNamedStyle(doc, err)
                if len(err) == 0:
                    mimeData.setData('application/qgis.style', doc.toByteArray())
                    mimeData.setText(doc.toString())
            return mimeData
    
    
        def currentComboBoxItem(self, cb):
            d = cb.itemData(cb.currentIndex(), Qt.UserRole)
            return d
    
    
            ui = self.ui
            r = None
            if ui.stackedWidget.currentWidget() == ui.pageMultiBand:
    
                r = self.rasterRendererMultiBand(ui)
    
    
            if ui.stackedWidget.currentWidget() == ui.pageSingleBand:
    
                r = self.rasterRendererSingleBand(ui)
            return r
    
        def rasterRendererMultiBand(self, ui):
            r = QgsMultiBandColorRenderer(None,
                                          ui.sliderRed.value(), ui.sliderGreen.value(), ui.sliderBlue.value())
            i = self.ui.comboBoxContrastEnhancement.currentIndex()
            alg = self.ui.comboBoxContrastEnhancement.itemData(i)
            if alg == QgsContrastEnhancement.NoEnhancement:
                r.setRedContrastEnhancement(None)
                r.setGreenContrastEnhancement(None)
                r.setBlueContrastEnhancement(None)
            else:
                rgbEnhancements = []
                for i in range(3):
                    e = QgsContrastEnhancement(self.sensor.bandDataType)
                    minmax = [float(self.multiBandMinValues[i].text()), float(self.multiBandMaxValues[i].text())]
                    cmin = min(minmax)
                    cmax = max(minmax)
                    e.setMinimumValue(cmin)
                    e.setMaximumValue(cmax)
                    e.setContrastEnhancementAlgorithm(alg)
                    rgbEnhancements.append(e)
                r.setRedContrastEnhancement(rgbEnhancements[0])
                r.setGreenContrastEnhancement(rgbEnhancements[1])
                r.setBlueContrastEnhancement(rgbEnhancements[2])
    
        def rasterRendererSingleBand(self, ui):
            r = QgsSingleBandPseudoColorRenderer(None, ui.sliderSingleBand.value(), None)
            minmax = [float(ui.tbSingleBandMin.text()), float(ui.tbSingleBandMax.text())]
            cmin = min(minmax)
            cmax = max(minmax)
            r.setClassificationMin(cmin)
            r.setClassificationMax(cmax)
            colorRamp = self.ui.cbSingleBandColorRamp.currentColorRamp()
            # fix: QGIS 3.0 constructor
            shaderFunc = QgsColorRampShader(cmin, cmax)
            shaderFunc.setColorRampType(self.currentComboBoxItem(ui.cbSingleBandColorRampType))
            shaderFunc.setClip(True)
            nSteps = 10
            colorRampItems = []
            diff = cmax - cmin
            for i in range(nSteps + 1):
                f = float(i) / nSteps
                color = colorRamp.color(f)
                value = cmin + diff * f
                colorRampItems.append(QgsColorRampShader.ColorRampItem(value, color))
            shaderFunc.setColorRampItemList(colorRampItems)
            shader = QgsRasterShader()
            shader.setMaximumValue(cmax)
            shader.setMinimumValue(cmin)
            shader.setRasterShaderFunction(shaderFunc)
            r.setShader(shader)
            return r
    
    class DatumViewUI(QFrame, loadUI('timeseriesdatumview.ui')):
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
        """
        Widget to host the MapCanvases of all map views that relate to a single Datum-Sensor combinbation.
        """
        def __init__(self, title='<#>', parent=None):
            super(DatumViewUI, self).__init__(parent)
            self.setupUi(self)
    
        def sizeHint(self):
            m = self.layout().contentsMargins()
    
            s = QSize(0, 0)
    
            map = None
            widgets = [self.layout().itemAt(i).widget() for i in range(self.layout().count())]
            widgets = [w for w in widgets if isinstance(w, QWidget)]
    
            maps = [w for w in widgets if isinstance(w, MapCanvas)]
            others = [w for w in widgets if not isinstance(w, MapCanvas)]
    
            s = self.layout().spacing()
            m = self.layout().contentsMargins()
    
            def totalHeight(widgetList):
                total = QSize(0,0)
                for w in widgetList:
                    ws = w.size()
                    if ws.width() == 0:
                        ws = w.sizeHint()
                    total.setWidth(max([total.width(), ws.width()]))
                    total.setHeight(total.height() +  ws.height())
                return total
    
            baseSize = totalHeight(widgets)
            if baseSize.width() == 0:
                for o in others:
                    baseSize.setWidth(9999)
            s = QSize(baseSize.width() + m.left() + m.right(),
                      baseSize.height() + m.top() + m.bottom())
            return s
    
    """
        def sizeHint(self):
    
            if not self.ui.isVisible():
                return QSize(0,0)
            else:
                #return self.ui.sizeHint()
    
                size = self.ui.sizeHint()
                s = self.ui.layout().spacing()
                m = self.ui.layout().contentsMargins()
                dx = m.left() + m.right() + s
                dy = self.ui.layout().spacing()
    
                n = len([m for m in self.mapCanvases.keys() if m.isVisible()])
                if n > 0:
                    baseSize = self.mapCanvases.values()[0].size()
                    size = QSize(baseSize.width()+ dx, \
                                 size.height()+ (n+1)*(dy+2*s))
                else:
                    s = ""
                return size
    
    
    """
    
    
    
    
    class DatumView(QObject):
    
    
        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, timeSeriesDatum, stv, parent=None):
    
            assert isinstance(timeSeriesDatum, TimeSeriesDatum)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
    
    
            super(DatumView, self).__init__()
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            self.ui = DatumViewUI(parent=parent)
    
            self.showLoading(False)
    
            self.wOffset = self.ui.layout().count()-1
    
            #self.minWidth = 50
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
            self.STV = stv
    
            self.TSD = timeSeriesDatum
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.scrollArea = stv.scrollArea
    
            self.Sensor = self.TSD.sensor
    
            self.Sensor.sigNameChanged.connect(lambda :self.setColumnInfo())
    
            self.TSD.sigVisibilityChanged.connect(self.setVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.MVC = stv.MVC
            self.DVC = stv.DVC
    
            self.mRenderState = dict()
    
            labelTxt = '{}\n{}'.format(str(self.TSD.date), self.TSD.sensor.name())
            tooltip = '{}'.format(self.TSD.pathImg)
    
    
            self.ui.labelTitle.setText(labelTxt)
            self.ui.labelTitle.setToolTip(tooltip)
    
    
        def setVisibility(self, b):
            self.ui.setVisible(b)
            self.sigVisibilityChanged.emit(b)
    
    
        def setHighlighted(self, b=True, timeout=1000):
            styleOn = """.QFrame {
                        border: 4px solid red;
                        border-radius: 4px;
                    }"""
            styleOff = """"""
            if b is True:
                self.ui.setStyleSheet(styleOn)
                if timeout > 0:
                    QTimer.singleShot(timeout, lambda : self.setHighlighted(b=False))
            else:
                self.ui.setStyleSheet(styleOff)
    
    
    
        def setMapViewVisibility(self, bandView, isVisible):
            self.mapCanvases[bandView].setVisible(isVisible)
    
    
        def removeMapView(self, mapView):
            canvas = self.mapCanvases.pop(mapView)
    
            self.ui.layout().removeWidget(canvas)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.adjustBaseMinSize()
    
            if self.ui.isVisible():
                for c in self.mapCanvases.values():
                    if c.isVisible():
    
    
        def insertMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            from timeseriesviewer.mapcanvas import MapCanvas
    
            mapCanvas = MapCanvas(self.ui)
    
            mapCanvas.setObjectName('MapCanvas {} {}'.format(mapView.title(), self.TSD.date))
    
            mapCanvas.blockSignals(True)
            self.registerMapCanvas(mapView, mapCanvas)
    
            # register MapCanvas on MV level
            mapView.registerMapCanvas(self.Sensor, mapCanvas)
            # register MapCanvas on STV level
            self.STV.registerMapCanvas(mapCanvas)
    
            mapCanvas.blockSignals(False)
    
            mapCanvas.renderComplete.connect(lambda : self.onRenderingChange(False))
            mapCanvas.renderStarting.connect(lambda : self.onRenderingChange(True))
    
    
            mapCanvas.sigDataLoadingFinished.connect(
                lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('Map {}'.format(self.Sensor.name()), dt))
            mapCanvas.sigDataLoadingFinished.connect(
                lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('All Sensors', dt))
    
    
        def showLoading(self, b):
            if b:
                self.ui.progressBar.setRange(0,0)
                self.ui.progressBar.setValue(-1)
            else:
                self.ui.progressBar.setRange(0,1)
                self.ui.progressBar.setValue(0)
    
        def onRenderingChange(self, b):
            mc = self.sender()
            #assert isinstance(mc, QgsMapCanvas)
            self.mRenderState[mc] = b
            self.showLoading(any(self.mRenderState.values()))
    
        def onRendering(self, *args):
            renderFlags = [m.renderFlag() for m in self.mapCanvases.values()]
            drawFlags = [m.isDrawing() for m in self.mapCanvases.values()]
    
            isLoading = any(renderFlags)
    
            self.showLoading(isLoading)
    
            s = ""
    
        def registerMapCanvas(self, mapView, mapCanvas):
    
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
            self.mapCanvases[mapView] = mapCanvas
    
    
            mapCanvas.layerModel().setRasterLayerSources([self.TSD.pathImg])