Skip to content
Snippets Groups Projects
mapcanvas.py 16.6 KiB
Newer Older
import os, logging

logger = logging.getLogger(__name__)

from qgis.core import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from timeseriesviewer import SETTINGS
from timeseriesviewer.utils import *
from timeseriesviewer.ui.widgets import TsvScrollArea

Benjamin Jakimow's avatar
Benjamin Jakimow committed

class TsvMapCanvas(QgsMapCanvas):
    from timeseriesviewer.main import SpatialExtent, SpatialPoint
    sigShowProfiles = pyqtSignal(SpatialPoint)
    #sigSpatialExtentChanged = pyqtSignal(SpatialExtent)

    def __init__(self, tsdView, mapView, parent=None):
        super(TsvMapCanvas, self).__init__(parent=parent)
        from timeseriesviewer.mapvisualization import TimeSeriesDatumView, MapView, SpatialTemporalVisualization
        assert isinstance(tsdView, TimeSeriesDatumView)
        assert isinstance(mapView, MapView)

        #the canvas
        self.setCrsTransformEnabled(True)
        self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
        self.setCanvasColor(SETTINGS.value('CANVAS_BACKGROUND_COLOR', QColor(0, 0, 0)))
        self.setContextMenuPolicy(Qt.DefaultContextMenu)


        self.mBlockExtentsChangedSignal = False

        self.scrollArea = tsdView.scrollArea
        assert isinstance(self.scrollArea, TsvScrollArea)
        self.scrollArea.sigResized.connect(self.setRenderMe)
        self.scrollArea.horizontalScrollBar().valueChanged.connect(self.setRenderMe)

        self.referenceLayer = QgsRasterLayer(self.tsdView.timeSeriesDatum.pathImg)

        self.spatTempVis = mapView.spatTempVis
        assert isinstance(self.spatTempVis, SpatialTemporalVisualization)
        self.setCrs(self.spatTempVis.crs())
        self.setSpatialExtent(self.spatTempVis.spatialExtent())
        #connect signals
        self.setFixedSize(self.spatTempVis.subsetSize())
        self.setSpatialExtent(self.spatTempVis.spatialExtent())
        self.spatTempVis.sigCRSChanged.connect(self.setCrs)
        self.spatTempVis.sigSpatialExtentChanged.connect(self.setSpatialExtent)
        self.spatTempVis.sigMapSizeChanged.connect(self.setFixedSize)
        self.spatTempVis.sigActivateMapTool.connect(self.activateMapTool)
        self.extentsChanged.connect(self.onExtentsChanged)
        #self.sigSpatialExtentChanged.connect(self.spatTempVis.setSpatialExtent)
        from timeseriesviewer.crosshair import CrosshairMapCanvasItem
        self.crosshairItem = CrosshairMapCanvasItem(self)


        self.vectorLayer = None

        self.mapView.sigVectorLayerChanged.connect(self.refresh)
        self.mapView.sigVectorVisibility.connect(self.refresh)
        self.renderMe = False
        self.setRenderMe()

        self.sensorView = self.mapView.sensorViews[self.tsdView.Sensor]
        self.mapView.sigMapViewVisibility.connect(self.refresh)
        self.mapView.sigCrosshairStyleChanged.connect(self.setCrosshairStyle)
        self.mapView.sigShowCrosshair.connect(self.setShowCrosshair)

        self.sensorView.sigSensorRendererChanged.connect(self.setRenderer)
        self.setRenderer(self.sensorView.layerRenderer())


        self.MAPTOOLS = dict()
        self.MAPTOOLS['zoomOut'] = QgsMapToolZoom(self, True)
        self.MAPTOOLS['zoomIn'] = QgsMapToolZoom(self, False)
        self.MAPTOOLS['pan'] = QgsMapToolPan(self)

        from timeseriesviewer.maptools import CursorLocationMapTool
        mt = CursorLocationMapTool(self)
        mt.sigLocationRequest.connect(self.sigShowProfiles.emit)
        self.MAPTOOLS['identifyProfile'] = mt
        mt = CursorLocationMapTool(self)
        mt.sigLocationRequest.connect(lambda pt: self.setCenter(pt))
        self.MAPTOOLS['moveCenter'] = mt
    def setFixedSize(self, size):
        assert isinstance(size, QSize)
        if self.size() != size:
            super(TsvMapCanvas, self).setFixedSize(size)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def setCrs(self, crs):
        assert isinstance(crs, QgsCoordinateReferenceSystem)
        if self.crs() != crs:
            print('Set MapCanvas CRS to {}'.format(crs.description()))
            self.mBlockExtentsChangedSignal = True
            self.setDestinationCrs(crs)
            self.mBlockExtentsChangedSignal = False
Benjamin Jakimow's avatar
Benjamin Jakimow committed

    def crs(self):
        return self.mapSettings().destinationCrs()

    def onExtentsChanged(self, *args):
        if not self.mBlockExtentsChangedSignal:
            self.mBlockExtentsChangedSignal = True
            print('set STV extent')
            self.spatTempVis.setSpatialExtent(self.spatialExtent())
            self.mBlockExtentsChangedSignal = False

    def mapLayersToRender(self, *args):
        """Returns the map layers actually to be rendered"""
        mapLayers = []

        if self.mapView.visibleVectorOverlay():
            #if necessary, register new vector layer
            refLyr = self.mapView.vectorLayer
            refUri = refLyr.dataProvider().dataSourceUri()

            if self.vectorLayer is None or self.vectorLayer.dataProvider().dataSourceUri() != refUri:
                providerKey = refLyr.dataProvider().name()
                baseName = os.path.basename(refUri)
                self.vectorLayer = QgsVectorLayer(refUri, baseName, providerKey)

            #update layer style
            self.vectorLayer.setRendererV2(refLyr.rendererV2().clone())
            mapLayers.append(self.vectorLayer)

        if self.referenceLayer:
            mapLayers.append(self.referenceLayer)

        return mapLayers



    def setLayerSet(self, *args):
        raise DeprecationWarning()

    def setLayers(self, mapLayers):
        reg = QgsMapLayerRegistry.instance()
        for l in mapLayers:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            reg.addMapLayer(l, False)
        super(TsvMapCanvas,self).setLayerSet([QgsMapCanvasLayer(l) for l in mapLayers])

    def refresh(self):
        self.setLayers(self.mapLayersToRender())
        self.setRenderMe()
        super(TsvMapCanvas, self).refresh()


    def setCrosshairStyle(self,crosshairStyle):
        from timeseriesviewer.crosshair import CrosshairStyle
        if crosshairStyle is None:
            self.crosshairItem.crosshairStyle.setShow(False)
            self.crosshairItem.update()
            assert isinstance(crosshairStyle, CrosshairStyle)
            self.crosshairItem.setCrosshairStyle(crosshairStyle)

    def setShowCrosshair(self,b):
        self.crosshairItem.setShow(b)

    def setRenderMe(self):
        oldFlag = self.renderFlag()

        newFlag = self.visibleRegion().boundingRect().isValid() \
                  and self.isVisible() \
                  and self.tsdView.timeSeriesDatum.isVisible()

        if oldFlag != newFlag:
            self.setRenderFlag(newFlag)
        #print((self.tsdView.TSD, self.renderFlag()))
        #return b.isValid()

    def pixmap(self):
        """
        Returns the current map image as pixmap
        :return:
        """
        return QPixmap(self.map().contentImage().copy())

    def contextMenuEvent(self, event):
        menu = QMenu()
        # add general options
        menu.addSeparator()
        action = menu.addAction('Stretch using current Extent')
        action.triggered.connect(self.stretchToCurrentExtent)
        action = menu.addAction('Zoom to Layer')
        action.triggered.connect(lambda : self.setExtent(SpatialExtent(self.referenceLayer.crs(),self.referenceLayer.extent()).toCrs(self.mapSettings().destinationCrs())
                                                         ))
        action = menu.addAction('Change crosshair style')
        from timeseriesviewer.crosshair import CrosshairDialog
        action.triggered.connect(lambda : self.setCrosshairStyle(
                CrosshairDialog.getCrosshairStyle(parent=self,
                                                mapCanvas=self,
                                                crosshairStyle=self.crosshairItem.crosshairStyle)
                ))

        if self.crosshairItem.crosshairStyle.mShow:
            action = menu.addAction('Hide crosshair')
            action.triggered.connect(lambda : self.setShowCrosshair(False))
        else:
            action = menu.addAction('Show crosshair')
            action.triggered.connect(lambda: self.setShowCrosshair(True))

        menu.addSeparator()

        m = menu.addMenu('Copy...')
        action = m.addAction('Map to Clipboard')
        action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
        action = m.addAction('Image Path')
        action.triggered.connect(lambda: QApplication.clipboard().setText(self.tsdView.TSD.pathImg))
        action = m.addAction('Image Style')
        #action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.tsdView.TSD.pathImg))

        m = menu.addMenu('Save as...')
        action = m.addAction('PNG')
        action.triggered.connect(lambda : self.saveMapImageDialog('PNG'))
        action = m.addAction('JPEG')
        action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))

        from timeseriesviewer.main import QgisTsvBridge
        bridge = QgisTsvBridge.instance()
        if bridge:
            assert isinstance(bridge, QgisTsvBridge)
            action = m.addAction('Add layer to QGIS')
            action = m.addAction('Import extent from QGIS')
            action = m.addAction('Export extent to QGIS')
            s = ""




        menu.addSeparator()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        TSD = self.tsdView.timeSeriesDatum
        action = menu.addAction('Hide date')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        action.triggered.connect(lambda : TSD.setVisibility(False))
        action = menu.addAction('Remove date')
        action.triggered.connect(lambda: TSD.timeSeries.removeDates([TSD]))
        action = menu.addAction('Remove map view')
        action.triggered.connect(lambda: self.mapView.sigRemoveMapView.emit(self.mapView))
        action = menu.addAction('Hide map view')
        action.triggered.connect(lambda: self.mapView.sigHideMapView.emit())


        menu.exec_(event.globalPos())

    def stretchToCurrentExtent(self):
        results = dict()
        se = self.spatialExtent()

        for l in self.layers():
            if isinstance(l, QgsRasterLayer):
                r = l.renderer()
                dp = l.dataProvider()
                newRenderer = None

                extent = se.toCrs(l.crs())

                assert isinstance(dp, QgsRasterDataProvider)
                bands = None
                if isinstance(r, QgsMultiBandColorRenderer):

                    def getCE(band, ce):
                        stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 500)
                        ce = QgsContrastEnhancement(ce)
                        ce.setMinimumValue(stats.minimumValue)
                        ce.setMaximumValue(stats.maximumValue)
                        return ce

                    newRenderer = QgsMultiBandColorRenderer(None,r.redBand(), r.greenBand(), r.blueBand())
                    newRenderer.setRedContrastEnhancement(getCE(r.redBand(), r.redContrastEnhancement()))
                    newRenderer.setGreenContrastEnhancement(getCE(r.greenBand(), r.greenContrastEnhancement()))
                    newRenderer.setBlueContrastEnhancement(getCE(r.blueBand(), r.blueContrastEnhancement()))

                    results[self.tsdView.timeSeriesDatum.sensor] = newRenderer
                elif isinstance(r, QgsSingleBandPseudoColorRenderer):
                    newRenderer = r.clone()
                    stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)
                    shader = newRenderer.shader()
                    newRenderer.setClassificationMax(stats.maximumValue)
                    newRenderer.setClassificationMin(stats.minimumValue)
                    shader.setMaximumValue(stats.maximumValue)
                    shader.setMinimumValue(stats.minimumValue)
                    s = ""

                if newRenderer is not None:
                    self.sensorView.setLayerRenderer(newRenderer)
        s = ""

    def activateMapTool(self, key):
        if key is None:
            self.setMapTool(None)
        elif key in self.MAPTOOLS.keys():
            self.setMapTool(self.MAPTOOLS[key])
        else:
            logger.error('unknown map tool key "{}"'.format(key))

    def saveMapImageDialog(self, fileType):
        lastDir = SETTINGS.value('CANVAS_SAVE_IMG_DIR', os.path.expanduser('~'))
        path = jp(lastDir, '{}.{}.{}'.format(self.tsdView.TSD.date, self.mapView.title(), fileType.lower()))

        path = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
        if len(path) > 0:
            self.saveAsImage(path, None, fileType)
            SETTINGS.setValue('CANVAS_SAVE_IMG_DIR', os.path.dirname(path))


    def setRenderer(self, renderer, targetLayerUri=None):
        if targetLayerUri is None:
            targetLayerUri = str(self.referenceLayer.source())

        lyrs = [l for l in self.mapLayersToRender() if str(l.source()) == targetLayerUri]
        assert len(lyrs) <= 1
        for lyr in lyrs:
            if isinstance(renderer, QgsMultiBandColorRenderer):
                r = renderer.clone()
                r.setInput(lyr.dataProvider())
            elif isinstance(renderer, QgsSingleBandPseudoColorRenderer):
                r = renderer.clone()
                #r = QgsSingleBandPseudoColorRenderer(None, renderer.band(), None)
                r.setInput(lyr.dataProvider())
                cmin = renderer.classificationMin()
                cmax = renderer.classificationMax()
                r.setClassificationMin(cmin)
                r.setClassificationMax(cmax)
                #r.setShader(renderer.shader())
                s = ""
            else:
                raise NotImplementedError()
            lyr.setRenderer(r)

        self.refresh()

    def setSpatialExtent(self, spatialExtent):
        assert isinstance(spatialExtent, SpatialExtent)
        if not self.mBlockExtentsChangedSignal and self.spatialExtent() != spatialExtent:

            spatialExtent = spatialExtent.toCrs(self.crs())
            print('MapCanvas {} set Spatial extent'.format(self.tsdView.timeSeriesDatum.date, spatialExtent))
            self.setExtent(spatialExtent)
            self.blockSignals(False)
            self.refresh()


    def spatialExtent(self):
        return SpatialExtent.fromMapCanvas(self)




class CanvasBoundingBoxItem(QgsGeometryRubberBand):

    def __init__(self, mapCanvas):
        assert isinstance(mapCanvas, QgsMapCanvas)
        super(CanvasBoundingBoxItem, self).__init__(mapCanvas)

        self.canvas = mapCanvas
        self.mCanvasExtents = dict()
        self.mShow = True
        self.mShowTitles = True
        self.setIconType(QgsGeometryRubberBand.ICON_NONE)

    def connectCanvas(self, canvas):
        assert isinstance(canvas, QgsMapCanvas)
        assert canvas != self.canvas
        if canvas not in self.mCanvasExtents.keys():
            self.mCanvasExtents[canvas] = None
            canvas.extentsChanged.connect(lambda : self.onExtentsChanged(canvas))
            canvas.destroyed.connect(lambda : self.disconnectCanvas(canvas))
            self.onExtentsChanged(canvas)

    def disconnectCanvas(self, canvas):
            self.mCanvasExtents.pop(canvas)

    def onExtentsChanged(self, canvas):
        assert isinstance(canvas, QgsMapCanvas)

        ext = SpatialExtent.fromMapCanvas(canvas)
        ext = ext.toCrs(self.canvas.mapSettings().destinationCrs())

        geom = QgsPolygonV2()
        assert geom.fromWkt(ext.asWktPolygon())

        self.mCanvasExtents[canvas] = (ext, geom)
        self.refreshExtents()

    def refreshExtents(self):
        multi = QgsMultiPolygonV2()
        if self.mShow:
            for canvas, t in self.mCanvasExtents.items():
                ext, geom = t
                multi.addGeometry(geom.clone())
        self.setGeometry(multi)

    def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
        super(CanvasBoundingBoxItem, self).paint(painter)

        if self.mShowTitles and self.mShow:
            painter.setPen(Qt.blue);
            painter.setFont(QFont("Arial", 30))

            for canvas, t in self.mCanvasExtents.items():
                ext, geom = t
                ULpx = self.toCanvasCoordinates(ext.center())
                txt = canvas.windowTitle()
                painter.drawLine(0, 0, 200, 200);
                painter.drawText(ULpx,  txt)


    def setShow(self, b):
        assert isinstance(b, bool)
        self.mShow = b

    def setShowTitles(self, b):
        assert isinstance(b, bool)
        self.mShowTitles = b

if __name__ == '__main__':
    import site, sys
    #add site-packages to sys.path as done by enmapboxplugin.py

    from timeseriesviewer import sandbox
    qgsApp = sandbox.initQgisEnvironment()



    qgsApp.exec_()
    qgsApp.exitQgis()