Skip to content
Snippets Groups Projects
mapvisualization.py 101 KiB
Newer Older
            return btn

        self.btnDefault = addBtnAction(self.actionSetDefault)
        self.btnTrueColor = addBtnAction(self.actionSetTrueColor)
        self.btnCIR = addBtnAction(self.actionSetCIR)
        self.btn453 = addBtnAction(self.actionSet453)

        b = self.wavelengths is not None
        for a in [self.actionSetCIR, self.actionSet453, self.actionSetTrueColor]:
            a.setEnabled(b)


class MapView(QObject):

    sigRemoveMapView = pyqtSignal(object)
    sigMapViewVisibility = pyqtSignal(bool)
    #sigVectorVisibility = pyqtSignal(bool)
    #sigRasterVisibility = pyqtSignal(bool)
    sigTitleChanged = pyqtSignal(str)
    sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    def __init__(self, mapViewCollectionDock, name='Map View', recommended_bands=None, parent=None):
        assert isinstance(mapViewCollectionDock, MapViewCollectionDock)
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        self.ui = MapViewUI(mapViewCollectionDock.stackedWidget)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        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.onCrosshairChanged(getCrosshairStyle(
Benjamin Jakimow's avatar
Benjamin Jakimow committed
                parent=self.ui,
        self.mapViewCollection = mapViewCollectionDock
        self.mSensorViews = collections.OrderedDict()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.mIsVisible = True

        self.ui.actionToggleVectorVisibility.toggled.connect(self.setVectorVisibility)
        self.ui.actionToggleRasterVisibility.toggled.connect(self.setRasterVisibility)
        self.ui.actionToggleCrosshairVisibility.toggled.connect(self.onCrosshairChanged)
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        self.ui.actionToggleMapViewHidden.toggled.connect(lambda b: self.setIsVisible(not b))
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        self.ui.actionToggleVectorVisibility.setChecked(False)
        self.ui.actionToggleRasterVisibility.setChecked(True)

        self.ui.actionSetVectorStyle.triggered.connect(self.setVectorLayerStyle)

        for sensor in self.mapViewCollection.TS.Sensors:
            self.addSensor(sensor)

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's avatar
Benjamin Jakimow committed

benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        for mapCanvas in self.mapCanvases():
            assert isinstance(mapCanvas, MapCanvas)
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
            if not mapCanvas.isVisible() == b:
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
                mapCanvas.setVisible(b)

        if self.ui.actionToggleMapViewHidden.isChecked() == b:
            self.ui.actionToggleMapViewHidden.setChecked(not b)

benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        if changed:
            self.sigMapViewVisibility.emit(b)


    def isVisible(self):
        return not self.ui.actionToggleMapViewHidden.isChecked()
        for sensor, sensorView in self.mSensorViews.items():
            m.extend(sensorView.mapCanvases())
        return m




    def setVectorLayerStyle(self, *args):
        if isinstance(self.mVectorLayer, QgsVectorLayer):
            d = QgsRendererPropertiesDialog(self.mVectorLayer, QgsStyle.defaultStyle())

            mc = self.mapCanvases()
            if len(mc) > 0:
                d.setMapCanvas(mc[0])
            s = ""
    def vectorLayerRenderer(self):
        if isinstance(self.mVectorLayer, QgsVectorLayer):
    def setVectorLayerRenderer(self, renderer):
        if isinstance(renderer, QgsFeatureRenderer) and \
            isinstance(self.mVectorLayer, QgsVectorLayer):
            self.mVectorLayer.setRendererV2(renderer)

        if isinstance(lyr, QgsVectorLayer) and self.ui.gbVectorRendering.isChecked():
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()
        for sensorView in self.mSensorViews.values():
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, sensor=None):

        if isinstance(sensor, SensorInstrument):
            sensorSettings = [self.mSensorViews[sensor]]
        else:
            #update all sensors
            sensorSettings = self.mSensorViews.values()
        for renderSetting in sensorSettings:
            assert isinstance(renderSetting, MapViewRenderSettings)
            renderSetting.applyStyle()

        #for mapCanvas in self.mapCanvases():
        #    assert isinstance(mapCanvas, MapCanvas)
        #    mapCanvas.refresh()
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)



    def rasterVisibility(self):
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        return self.ui.actionToggleRasterVisibility.isChecked()

    def vectorVisibility(self):
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        return self.ui.actionToggleVectorVisibility.isChecked()

    def setRasterVisibility(self, b):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(b, bool)

        self.mRastersVisible = b
        self.ui.actionToggleRasterVisibility.setChecked(b)

        for mapCanvas in self.mapCanvases():
            assert isinstance(mapCanvas, MapCanvas)
            mapCanvas.layerModel().setRasterLayerVisibility(b)
            mapCanvas.refresh()


    def setVectorVisibility(self, b):
        assert isinstance(b, bool)
        self.mVectorsVisible = b
        self.ui.actionToggleVectorVisibility.setChecked(b)
        for mapCanvas in self.mapCanvases():
            assert isinstance(mapCanvas, MapCanvas)
            mapCanvas.layerModel().setVectorLayerVisibility(self.mVectorsVisible)
            mapCanvas.refresh()
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        assert sensor in self.mSensorViews.keys()
        self.mSensorViews.pop(sensor)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.removeSensor(sensor)
        return True

    def hasSensor(self, sensor):
        assert type(sensor) is SensorInstrument
        return sensor in self.mSensorViews.keys()
    def registerMapCanvas(self, sensor, mapCanvas):
        from timeseriesviewer.mapcanvas import MapCanvas
        assert isinstance(mapCanvas, MapCanvas)
        assert isinstance(sensor, SensorInstrument)

        mapViewRenderSettings = self.mSensorViews[sensor]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        assert isinstance(mapViewRenderSettings, MapViewRenderSettings)
        mapViewRenderSettings.registerMapCanvas(mapCanvas)
        mapCanvas.setRenderer(mapViewRenderSettings.rasterRenderer())
        mapCanvas.setRenderer(self.vectorLayerRenderer())
        mapCanvas.sigCrosshairVisibilityChanged.connect(self.onCrosshairChanged)
        mapCanvas.sigCrosshairStyleChanged.connect(self.onCrosshairChanged)

        #register non-sensor specific signals for this mpa view
        self.sigMapViewVisibility.connect(mapCanvas.refresh)
        self.sigVectorLayerChanged.connect(mapCanvas.refresh)
#        self.sigVectorVisibility.connect(mapCanvas.refresh)
    def crosshairStyle(self)->CrosshairStyle:
        """
        Returns the CrosshairStyle
        :return:
        """
        for c in self.mapCanvases():
            assert isinstance(c, MapCanvas)
            style = c.crosshairStyle()
            if isinstance(style, CrosshairStyle):
                return style
        return None
    def onCrosshairChanged(self, obj):
        """
        Synchronizes all crosshair positions. Takes care of CRS differences.
        :param spatialPoint: SpatialPoint of the new Crosshair position
        """
        from timeseriesviewer.crosshair import CrosshairStyle

        srcCanvas = self.sender()
        if isinstance(srcCanvas, MapCanvas):
            dstCanvases = [c for c in self.mapCanvases() if c != srcCanvas]
        else:
            dstCanvases = [c for c in self.mapCanvases()]

        if isinstance(obj, bool):
            for mapCanvas in dstCanvases:
                mapCanvas.setCrosshairVisibility(obj, emitSignal=False)
        if isinstance(obj, CrosshairStyle):
            for mapCanvas in dstCanvases:
                mapCanvas.setCrosshairStyle(obj, emitSignal=False)
        if isinstance(sensor, SensorInstrument) and sensor not in self.mSensorViews.keys():
            #w.showSensorName(False)
            w = self.ui.addSensor(sensor)
            w.sigRendererChanged.connect(lambda s=sensor : self.refreshMapView(sensor=s))
            #w.sigSensorRendererChanged.connect(self.onSensorRenderingChanged)
            self.mSensorViews[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 sensorWidget(self, sensor):
        return self.mSensorViews[sensor]
def displayBandNames(provider_or_dataset, bands=None):
    results = None
    if isinstance(provider_or_dataset, QgsRasterLayer):
        return displayBandNames(provider_or_dataset.dataProvider())
    elif isinstance(provider_or_dataset, QgsRasterDataProvider):
        if provider_or_dataset.name() == 'gdal':
            ds = gdal.Open(provider_or_dataset.dataSourceUri())
            results = displayBandNames(ds, bands=bands)
        else:
            # same as in QgsRasterRendererWidget::displayBandName
            results = []
            if bands is None:
                bands = range(1, provider_or_dataset.bandCount() + 1)
            for band in bands:
                result = provider_or_dataset.generateBandName(band)
                colorInterp ='{}'.format(provider_or_dataset.colorInterpretationName(band))
                if colorInterp != 'Undefined':
                    result += '({})'.format(colorInterp)
                results.append(result)

    elif isinstance(provider_or_dataset, gdal.Dataset):
        results = []
        if bands is None:
            bands = range(1, provider_or_dataset.RasterCount+1)
        for band in bands:
            b = provider_or_dataset.GetRasterBand(band)
            descr = b.GetDescription()
            if len(descr) == 0:
                descr = 'Band {}'.format(band)
            results.append(descr)

    return results


class RasterDataProviderMockup(QgsRasterDataProvider):

    def __init__(self):
        super(RasterDataProviderMockup, self).__init__('')




Benjamin Jakimow's avatar
Benjamin Jakimow committed
class MapViewRenderSettings(QgsCollapsibleGroupBox, loadUI('mapviewrendersettings.ui')):


    LUT_RENDERER = {QgsMultiBandColorRenderer:QgsMultiBandColorRendererWidget,
                    QgsSingleBandGrayRenderer:QgsSingleBandGrayRendererWidget,
                    QgsSingleBandPseudoColorRenderer:QgsSingleBandPseudoColorRendererWidget,
                    QgsPalettedRasterRenderer:QgsPalettedRendererWidget}
    LUT_RENDERER[QgsMultiBandColorRenderer]=MultiBandColorRendererWidget
    LUT_RENDERER[QgsSingleBandPseudoColorRenderer]=SingleBandPseudoColorRendererWidget
    LUT_RENDERER[QgsSingleBandGrayRenderer]=SingleBandGrayRendererWidget
    LUT_RENDERER[QgsPalettedRasterRenderer] = PalettedRendererWidget
    def __init__(self, sensor, parent=None):
        """Constructor."""
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        super(MapViewRenderSettings, 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)

        QApplication.clipboard().dataChanged.connect(self.onClipboardChanged)

        assert isinstance(sensor, SensorInstrument)
        self.mSensor = sensor
        self.mSensor.sigNameChanged.connect(self.onSensorNameChanged)
        self.setTitle(self.mSensor.name())

        from timeseriesviewer.models import OptionListModel, Option
        rasterRendererModel = OptionListModel()
        #rasterRendererModel.addOption(Option(QgsMultiBandColorRendererWidget, name='multibandcolor (QGIS)', mRenderType = QgsMultiBandColorRenderer))
        #rasterRendererModel.addOption(Option(QgsPalettedRendererWidget, name='paletted (QGIS)', mRenderType = QgsPalettedRasterRenderer))
        #rasterRendererModel.addOption(Option(QgsSingleBandGrayRendererWidget, name='singlegray (QGIS)', mRenderType = QgsSingleBandGrayRenderer))
        #rasterRendererModel.addOption(Option(QgsSingleBandPseudoColorRendererWidget, name='singlebandpseudocolor (QGIS)', mRenderType = QgsSingleBandPseudoColorRenderer))

        rasterRendererModel.addOption(Option(MultiBandColorRendererWidget, name='Multibandcolor', mRenderType=QgsMultiBandColorRenderer))
        rasterRendererModel.addOption(Option(SingleBandGrayRendererWidget, name='Singlegray', mRenderType=QgsSingleBandGrayRenderer))
        rasterRendererModel.addOption(Option(SingleBandPseudoColorRendererWidget, name='Singleband Pseudocolor', mRenderType=QgsSingleBandPseudoColorRenderer))
        rasterRendererModel.addOption(Option(PalettedRendererWidget, name='Paletted', mRenderType=QgsPalettedRasterRenderer))
        self.mRasterRendererModel = rasterRendererModel

        self.cbRenderType.setModel(self.mRasterRendererModel)
        assert isinstance(self.stackedWidget, QStackedWidget)

        self.mMockupCanvas = QgsMapCanvas()
        self.mMockupRasterLayer = QgsRasterLayer(self.mSensor.pathImg)
        self.mMockupCanvas.setLayers([self.mMockupRasterLayer])
        for func in rasterRendererModel.optionValues():

            #extent = self.canvas.extent()
            #w = func.create(self.mMockupRasterLayer, self.mMockupRasterLayer.extent())
            w = func(self.mMockupRasterLayer, self.mMockupRasterLayer.extent())
            #w = func(QgsRasterLayer(), QgsRectangle())

            w.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
            #w.sizeHint = lambda : QSize(300, 50)
            w.setMapCanvas(self.mMockupCanvas)

            self.stackedWidget.addWidget(w)

        self.mMapCanvases = []
        self.initActions()

    def initActions(self):


        self.btnPasteStyle.setDefaultAction(self.actionPasteStyle)
        self.btnCopyStyle.setDefaultAction(self.actionCopyStyle)
        self.btnApplyStyle.setDefaultAction(self.actionApplyStyle)

        clipboardRenderer = rendererFromXml(QApplication.clipboard().mimeData())
        self.actionPasteStyle.setEnabled(isinstance(clipboardRenderer, QgsRasterRenderer))
        self.actionPasteStyle.triggered.connect(self.pasteStyleFromClipboard)
        self.actionCopyStyle.triggered.connect(self.pasteStyleToClipboard)
        self.actionApplyStyle.triggered.connect(self.applyStyle)

    def mapCanvases(self):
        return self.mMapCanvases[:]

    def registerMapCanvas(self, mapCanvas):

        assert isinstance(mapCanvas, MapCanvas)
        self.mMapCanvases.append(mapCanvas)
        mapCanvas.sigChangeSVRequest.connect(self.onMapCanvasRendererChangeRequest)

    def onMapCanvasRendererChangeRequest(self, mapCanvas, renderer):
        self.setRasterRenderer(renderer)
        self.applyStyle()

    def onSensorNameChanged(self, newName):
        self.setTitle(self.mSensor.name())
        self.actionApplyStyle.setToolTip('Apply style to all map view images from "{}"'.format(self.mSensor.name()))


    def currentRenderWidget(self):
        """
        Returns the current QgsRasterRendererWidget
        :return: QgsRasterRendererWidget
        """
        return self.stackedWidget.currentWidget()


    def setRasterRenderer(self, renderer):
        assert isinstance(renderer, QgsRasterRenderer)
        assert isinstance(self.stackedWidget, QStackedWidget)

        self.mMockupRasterLayer.setRenderer(renderer)

        #find the widget class that fits
        cls = None

        for option in self.mRasterRendererModel:
            if type(renderer) == option.mRenderType:
                cls = option.value()
                break
        if cls == None:
            return

        widgets = []
        for i in range(self.stackedWidget.count()):
            w = self.stackedWidget.widget(i)
            if isinstance(w, cls):
                widgets.append(w)

        if len(widgets) > 0:
            for w in widgets:
                assert isinstance(w, QgsRasterRendererWidget)

                #w.setRasterLayer(self.mMockupRasterLayer)
                #w.setFromRenderer(cloneRenderer(renderer))
                w.setFromRenderer(renderer)
                #w.doComputations()

            w = widgets[0]
            self.stackedWidget.setCurrentWidget(w)

            self.cbRenderType.setCurrentIndex(self.stackedWidget.currentIndex())


    def rasterRenderer(self):

        return self.stackedWidget.currentWidget().renderer()


    def apply(self):

        mRendererWidget = self.currentRenderWidget()
        mRendererWidget.doComputations()
    def onClipboardChanged(self):
        mimeData = QApplication.clipboard().mimeData()
        renderer = rendererFromXml(mimeData)
        b = isinstance(renderer, QgsRasterRenderer)
        #if b == False:
        #    print(mimeData.formats())
        #    s = ""
        self.actionPasteStyle.setEnabled(b)


    def pasteStyleFromClipboard(self):
        mimeData = QApplication.clipboard().mimeData()
        renderer = rendererFromXml(mimeData)
        if isinstance(renderer, QgsRasterRenderer):
            self.setRasterRenderer(renderer)

    def pasteStyleToClipboard(self):
        xml = rendererToXml(self.rasterRenderer())
        assert isinstance(xml, QDomDocument)
        md = QMimeData()
        #['application/qgis.style', 'text/plain']

        md.setData('application/qgis.style', xml.toByteArray())
        md.setData('text/plain', xml.toByteArray())
        QApplication.clipboard().setMimeData(md)
    def applyStyle(self, *args):

        r = self.rasterRenderer()
        for mapCanvas in self.mMapCanvases:
            assert isinstance(mapCanvas, MapCanvas)
            mapCanvas.layerModel().setRenderer(r)
            mapCanvas.refresh()




RENDER_CLASSES = {}
RENDER_CLASSES['rasterrenderer'] = {
    'singlebandpseudocolor':QgsSingleBandPseudoColorRenderer,
    'singlebandgray': QgsSingleBandGrayRenderer,
    'paletted':QgsPalettedRasterRenderer,
    'multibandcolor': QgsMultiBandColorRenderer,
    'hillshade': QgsHillshadeRenderer
}
RENDER_CLASSES['renderer-v2'] = {
    'categorizedSymbol':QgsCategorizedSymbolRenderer,
    'singleSymbol':QgsSingleSymbolRenderer
}


def rendererToXml(renderer):
    """
    Returns a renderer XML representation
    :param renderer: QgsRasterRender | QgsFeatureRenderer
    :return: QDomDocument
    """
    doc = QDomDocument()
    if isinstance(renderer, QgsRasterRenderer):
        #create a dummy raster layer
        import uuid
        from timeseriesviewer.virtualrasters import write_vsimem, read_vsimem
        xml = """<VRTDataset rasterXSize="1" rasterYSize="1">
                  <GeoTransform>  0.0000000000000000e+00,  1.0000000000000000e+00,  0.0000000000000000e+00,  0.0000000000000000e+00,  0.0000000000000000e+00, -1.0000000000000000e+00</GeoTransform>
                  <VRTRasterBand dataType="Float32" band="1">
                    <Metadata>
                      <MDI key="STATISTICS_MAXIMUM">0</MDI>
                      <MDI key="STATISTICS_MEAN">0</MDI>
                      <MDI key="STATISTICS_MINIMUM">0</MDI>
                      <MDI key="STATISTICS_STDDEV">0</MDI>
                    </Metadata>
                    <Description>Band 1</Description>
                    <Histograms>
                      <HistItem>
                        <HistMin>0</HistMin>
                        <HistMax>0</HistMax>
                        <BucketCount>1</BucketCount>
                        <IncludeOutOfRange>0</IncludeOutOfRange>
                        <Approximate>0</Approximate>
                        <HistCounts>0</HistCounts>
                      </HistItem>
                    </Histograms>
                  </VRTRasterBand>
                </VRTDataset>
                """
        path = '/vsimem/{}.vrt'.format(uuid.uuid4())
        drv = gdal.GetDriverByName('VRT')
        assert isinstance(drv, gdal.Driver)
        write_vsimem(path, xml)
        lyr = QgsRasterLayer(path)
        assert lyr.isValid()
        lyr.setRenderer(renderer.clone())
        #remove dummy raster layer
        lyr = None
        drv.Delete(path)

    elif isinstance(renderer, QgsFeatureRenderer):
        #todo: distinguish vector type from requested renderer
        lyr = QgsVectorLayer('Point?crs=epsg:4326&field=id:integer', 'dummy', 'memory')
        lyr.setRenderer(renderer.clone())
    else:
        raise NotImplementedError()
    return doc


def rendererFromXml(xml):
    """
    Reads a string `text` and returns the first QgsRasterRenderer or QgsFeatureRenderer (if defined).
    :param text:
    :return:
    """

    if isinstance(xml, QMimeData):
        for format in ['application/qgis.style', 'text/plain']:
            if format in xml.formats():
                dom  = QDomDocument()
                dom.setContent(xml.data(format))
                return rendererFromXml(dom)
        return None

    elif isinstance(xml, str):
        dom = QDomDocument()
        dom.setContent(xml)
        return rendererFromXml(dom)

    assert isinstance(xml, QDomDocument)
    root = xml.documentElement()
    for baseClass, renderClasses in RENDER_CLASSES.items():
        elements = root.elementsByTagName(baseClass)
        if elements.count() > 0:
            elem = elements.item(0).toElement()
            typeName = elem.attributes().namedItem('type').nodeValue()
            if typeName in renderClasses.keys():
                rClass = renderClasses[typeName]
                if baseClass == 'rasterrenderer':

                    return rClass.create(elem, DUMMY_RASTERINTERFACE)
                elif baseClass == 'renderer-v2':
                    context = QgsReadWriteContext()
                    return rClass.load(elem, context)
                    #return rClass.create(elem)
            else:
                print(typeName)
                s =""
    return None



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()
                if w.isVisible():
                    total.setWidth(max([total.width(), ws.width()]))
                    total.setHeight(total.height() +  ws.height())
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
            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())
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        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.mSensor = self.TSD.sensor
        self.mSensor.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.mMapCanvases = dict()
        self.mRenderState = dict()
        labelTxt = '{}\n{}'.format(str(self.TSD.date), self.TSD.sensor.name())
        tooltip = '{}'.format(self.TSD.pathImg)
    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)

        canvas = self.mMapCanvases.pop(mapView)
        self.ui.layout().removeWidget(canvas)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #self.adjustBaseMinSize()
            for c in self.mMapCanvases.values():

    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)
        mapCanvas.setMapView(mapView)
        mapCanvas.setTSD(self.TSD)
        self.registerMapCanvas(mapView, mapCanvas)
        # register MapCanvas on MV level
        mapView.registerMapCanvas(self.mSensor, 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))

            lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('Map {}'.format(self.mSensor.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.mMapCanvases.values()]
        drawFlags = [m.isDrawing() for m in self.mMapCanvases.values()]
        isLoading = any(renderFlags)

        self.showLoading(isLoading)

        s = ""
    def registerMapCanvas(self, mapView, mapCanvas):
        from timeseriesviewer.mapcanvas import MapCanvas
        assert isinstance(mapCanvas, MapCanvas)
        assert isinstance(mapView, MapView)
        self.mMapCanvases[mapView] = mapCanvas
        #mapView.sigTitleChanged.connect(lambda title : mapCanvas.setSaveFileName('{}_{}'.format(self.TSD.date, title)))
        mapCanvas.layerModel().setRasterLayerSources([self.TSD.pathImg])

        #self.ui.layout().insertWidget(self.wOffset + len(self.mapCanvases), mapCanvas)
        self.ui.layout().insertWidget(self.ui.layout().count() - 1, mapCanvas)
        self.ui.update()

        #register signals handled on (this) DV level
        mapCanvas.renderStarting.connect(lambda: self.sigLoadingStarted.emit(mapView, self.TSD))
        mapCanvas.mapCanvasRefreshed.connect(lambda: self.sigLoadingFinished.emit(mapView, self.TSD))
        mapCanvas.sigShowProfiles.connect(lambda c, t : mapView.sigShowProfiles.emit(c,mapCanvas, t))
        mapCanvas.sigChangeDVRequest.connect(self.onMapCanvasRequest)
    def onMapCanvasRequest(self, mapCanvas, key):
        if key == 'hide_date':
            self.TSD.setVisibility(False)
        if key == 'copy_sensor':
            QApplication.clipboard().setText(self.TSD.sensor.name())
        if key == 'copy_date':
            QApplication.clipboard().setText(str(self.TSD.date))
        if key == 'copy_path':
            QApplication.clipboard().setText(str(self.TSD.pathImg))
    def __lt__(self, other):
        assert isinstance(other, DatumView)
        return self.TSD < other.TSD
        assert isinstance(other, DatumView)
        return self.TSD == other.TSD
    sigLoadingStarted = pyqtSignal(DatumView, MapView)
    sigLoadingFinished = pyqtSignal(DatumView, MapView)
    sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    sigShowMapLayerInfo = pyqtSignal(dict)
    sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
    sigMapSizeChanged = pyqtSignal(QSize)
    sigCRSChanged = pyqtSignal(QgsCoordinateReferenceSystem)
    sigActivateMapTool = pyqtSignal(str)

    def __init__(self, timeSeriesViewer):
        super(SpatialTemporalVisualization, self).__init__()
        #assert isinstance(timeSeriesViewer, TimeSeriesViewer), timeSeriesViewer
        self.mBlockCanvasSignals = False
        self.mSpatialExtent = SpatialExtent.world()
        self.mCRS = self.mSpatialExtent.crs()
        self.mSize = QSize(200,200)
        self.mColor = Qt.black
        self.mMapCanvases = []
Benjamin Jakimow's avatar
Benjamin Jakimow committed

        #map-tool handling
        self.mMapToolActivator = None
        self.mMapTools = []
        assert isinstance(self.scrollArea, MapViewScrollArea)


        self.mRefreshTimer = QTimer(self)
        self.mRefreshTimer.setInterval(1000)
        self.mRefreshTimer.timeout.connect(self.refresh)

        self.scrollArea.sigResized.connect(self.mRefreshTimer.start)
        self.scrollArea.horizontalScrollBar().valueChanged.connect(self.mRefreshTimer.start)
        self.TSV = timeSeriesViewer
        self.TS = timeSeriesViewer.TS
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.ui.dockMapViews.setTimeSeries(self.TS)
        self.targetLayout = self.ui.scrollAreaSubsetContent.layout()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        #self.MVC = MapViewCollection(self)
        #self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
        self.MVC = self.ui.dockMapViews
benjamin.jakimow@geo.hu-berlin.de's avatar
benjamin.jakimow@geo.hu-berlin.de committed
        assert isinstance(self.MVC, MapViewCollectionDock)
        self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
        self.MVC.sigMapViewAdded.connect(self.onMapViewAdded)
        self.DVC = DateViewCollection(self)
        self.DVC.sigResizeRequired.connect(self.adjustScrollArea)
        #self.DVC.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
        #self.DVC.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
        #self.timeSeriesDateViewCollection.sigSpatialExtentChanged.connect(self.setSpatialExtent)
        self.TS.sigTimeSeriesDatesAdded.connect(self.DVC.addDates)
        self.TS.sigTimeSeriesDatesRemoved.connect(self.DVC.removeDates)
        self.DVC.addDates(self.TS[:])
        if len(self.TS) > 0:
            self.setSpatialExtent(self.TS.getMaxSpatialExtent())
        #self.setSubsetSize(QSize(100,50))

    def mapViewFromCanvas(self, mapCanvas:MapCanvas)->MapView:
        """
        Returns the MapView a mapCanvas belongs to
        :param mapCanvas: MapCanvas
        :return: MapView
        """
        for mapView in self.MVC:
            assert isinstance(mapView, MapView)
            if mapCanvas in mapView.mapCanvases():
                return mapView
        return None

    def onMapViewAdded(self, *args):
        self.adjustScrollArea()
        s = ""
    def registerMapCanvas(self, mapCanvas):
        from timeseriesviewer.mapcanvas import MapCanvas
        assert isinstance(mapCanvas, MapCanvas)