Skip to content
Snippets Groups Projects
mapcanvas.py 52.9 KiB
Newer Older
  • Learn to ignore specific revisions
  •         action = menu.addAction('Refresh')
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            action.triggered.connect(lambda: self.refresh())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            m = menu.addMenu('Crosshair...')
            action = m.addAction('Show')
            action.setCheckable(True)
            action.setChecked(self.mCrosshairItem.visibility())
            action.toggled.connect(self.setCrosshairVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            action = m.addAction('Set Style')
    
            def onCrosshairChange(*args):
    
                style = CrosshairDialog.getCrosshairStyle(parent=self,
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                                                          mapCanvas=self,
    
                                                          crosshair=self.mCrosshairItem.crosshairStyle)
    
    
                if isinstance(style, CrosshairStyle):
                    self.setCrosshairStyle(style)
    
            action.triggered.connect(onCrosshairChange)
    
            if isinstance(tsd, TimeSeriesDate):
    
                menu.addSeparator()
                m = menu.addMenu('Copy...')
                action = m.addAction('Date')
                action.triggered.connect(lambda: QApplication.clipboard().setText(str(tsd.date())))
    
                action.setToolTip('Sends "{}" to the clipboard.'.format(str(tsd.date())))
    
                action = m.addAction('Sensor')
                action.triggered.connect(lambda: QApplication.clipboard().setText(tsd.sensor().name()))
    
                action.setToolTip('Sends "{}" to the clipboard.'.format(tsd.sensor().name()))
    
                action = m.addAction('Path')
    
                paths = [QDir.toNativeSeparators(p) for p in tsd.sourceUris()]
                action.triggered.connect(lambda _, paths=paths: QApplication.clipboard().setText('\n'.join(paths)))
    
                action.setToolTip('Sends {} source URI(s) to the clipboard.'.format(len(tsd)))
    
    
                extent = self.extent()
                assert isinstance(extent, QgsRectangle)
                action = m.addAction('Extent')
                action.triggered.connect(lambda _, extent=extent: QApplication.clipboard().setText(extent.toString()))
                action.setToolTip('Sends the map extent to the clipboard.')
    
    
                action = m.addAction('Map')
                action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
                action.setToolTip('Copies this map into the clipboard.')
    
                from .utils import findParent
                from .mapvisualization import MapWidget
                mw = findParent(self, MapWidget)
                if isinstance(mw, MapWidget):
                    action = m.addAction('All Maps')
                    action.triggered.connect(lambda: QApplication.clipboard().setPixmap(mw.grab()))
                    action.setToolTip('Copies all maps into the clipboard.')
    
    
    
            m = menu.addMenu('Map Coordinates...')
    
            ext = self.spatialExtent()
            center = self.spatialExtent().spatialCenter()
            action = m.addAction('Extent (WKT Coordinates)')
            action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktCoordinates()))
            action = m.addAction('Extent (WKT Polygon)')
            action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
    
            m.addSeparator()
    
    
            action = m.addAction('Map Center (WKT)')
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            action.triggered.connect(lambda: QApplication.clipboard().setText(center.asWkt()))
    
            action = m.addAction('Map Center')
    
            action.triggered.connect(lambda: QApplication.clipboard().setText(center.toString()))
    
    
            action = m.addAction('Map Extent (WKT)')
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
    
    
            action = m.addAction('Map Extent')
            action.triggered.connect(lambda: QApplication.clipboard().setText(ext.toString()))
    
    
            m.addSeparator()
    
            action = m.addAction('CRS (EPSG)')
            action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().authid()))
            action = m.addAction('CRS (WKT)')
            action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toWkt()))
            action = m.addAction('CRS (Proj4)')
            action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toProj4()))
    
    
            m = menu.addMenu('Save to...')
    
            action.triggered.connect(lambda: self.saveMapImageDialog('PNG'))
    
            action = m.addAction('JPEG')
            action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))
    
            menu.addSeparator()
    
    
            classSchemes = []
            for layer in lyrWithSelectedFeaturs:
                for classScheme in layerClassSchemes(layer):
                    assert isinstance(classScheme, ClassificationScheme)
                    if classScheme in classSchemes:
                        continue
    
                    classMenu = m.addMenu('Classification "{}"'.format(classScheme.name()))
                    assert isinstance(classMenu, QMenu)
                    for classInfo in classScheme:
                        assert isinstance(classInfo, ClassInfo)
                        a = classMenu.addAction(classInfo.name())
                        a.setIcon(classInfo.icon())
    
                        a.setToolTip('Write "{}" or "{}" to connected vector field attributes'.format(classInfo.name(), classInfo.label()))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                            lambda *args, tsd=self.tsd(), ci = classInfo:
    
                            applyShortcutsToRegisteredLayers(tsd, [ci]))
                    classSchemes.append(classScheme)
    
            if isinstance(self.tsd(), TimeSeriesDate) and isinstance(eotsv, TimeSeriesViewer):
    
                menu.addSeparator()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                action = menu.addAction('Focus on Spatial Extent')
    
                action.triggered.connect(ts.focusVisibilityToExtent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                action = menu.addAction('Hide Date')
    
                action.triggered.connect(lambda *args: ts.hideTSDs([tsd]))
    
                action = menu.addAction('Remove Date')
                action.triggered.connect(lambda *args, : ts.removeTSDs([tsd]))
    
        def onSetLayerProperties(self, lyr:QgsRasterLayer, canvas:QgsMapCanvas):
            # b = isinstance(mapLayer, SensorProxyLayer) == False:
    
            result = showLayerPropertiesDialog(lyr, canvas, useQGISDialog=True)
    
            if result == QDialog.Accepted and isinstance(lyr, SensorProxyLayer):
                r = lyr.renderer().clone()
                proxyLayer = self.mMapView.sensorProxyLayer(lyr.sensor())
                r.setInput(proxyLayer.dataProvider())
                proxyLayer.setRenderer(r)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onPasteStyleFromClipboard(self, lyr):
            from .externals.qps.layerproperties import pasteStyleFromClipboard
            pasteStyleFromClipboard(lyr)
            if isinstance(lyr, SensorProxyLayer):
    
                r = lyr.renderer().clone()
                proxyLayer = self.mMapView.sensorProxyLayer(lyr.sensor())
                r.setInput(proxyLayer.dataProvider())
                proxyLayer.setRenderer(r)
    
    
        def contextMenuEvent(self, event:QContextMenuEvent):
    
            """
            Create and shows the MapCanvas context menu.
            :param event: QEvent
            """
    
            assert isinstance(event, QContextMenuEvent)
            menu = self.contextMenu(event.pos())
    
        def addLayers2QGIS(self, mapLayers):
    
            from eotimeseriesviewer.utils import qgisInstance
    
            iface = qgisInstance()
            if isinstance(iface, QgisInterface):
                grpNode= iface.layerTreeView().currentGroupNode()
                assert isinstance(grpNode, QgsLayerTreeGroup)
                for l in mapLayers:
                    if isinstance(l, QgsRasterLayer):
                        lqgis = iface.addRasterLayer(l.source(), l.name())
                        lqgis.setRenderer(l.renderer().clone())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                    if isinstance(l, QgsVectorLayer):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        lqgis = iface.addVectorLayer(l.source(), l.name(), 'ogr')
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        lqgis.setRenderer(l.renderer().clone())
    
        def stretchToCurrentExtent(self):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Stretches the top-raster layer band to the current spatial extent
            """
    
            self.stretchToExtent(se, stretchType='linear_minmax', p=0.05)
    
        def stretchToExtent(self, spatialExtent:SpatialExtent, stretchType='linear_minmax', layer:QgsRasterLayer=None, **stretchArgs):
    
            """
            :param spatialExtent: rectangle to get the image statistics for
            :param stretchType: ['linear_minmax' (default), 'gaussian']
            :param stretchArgs:
                linear_minmax: 'p'  percentage from min/max, e.g. +- 5 %
                gaussian: 'n' mean +- n* standard deviations
            :return:
            """
    
            if not isinstance(layer, QgsRasterLayer):
                layers = [l for l in self.layers() if isinstance(l, SensorProxyLayer)]
                if len(layers) > 0:
                    layer = layers[0]
                else:
                    layers = [l for l in self.layers() if isinstance(l, SensorProxyLayer)]
                    if len(layers) > 0:
                        layer = layers[0]
    
            if not isinstance(layer, QgsRasterLayer):
                return
    
            r = layer.renderer()
            dp = layer.dataProvider()
            newRenderer = None
            extent = spatialExtent.toCrs(layer.crs())
    
            assert isinstance(dp, QgsRasterDataProvider)
    
            def getCE(band):
                stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 256)
    
                ce = QgsContrastEnhancement(dp.dataType(band))
                d = (stats.maximumValue - stats.minimumValue)
                if stretchType == 'linear_minmax':
                    ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
                    ce.setMinimumValue(stats.minimumValue + d * stretchArgs.get('p', 0))
                    ce.setMaximumValue(stats.maximumValue - d * stretchArgs.get('p', 0))
                elif stretchType == 'gaussian':
                    ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
                    ce.setMinimumValue(stats.mean - stats.stdDev * stretchArgs.get('n', 3))
                    ce.setMaximumValue(stats.mean + stats.stdDev * stretchArgs.get('n', 3))
                else:
                    # stretchType == 'linear_minmax':
                    ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
                    ce.setMinimumValue(stats.minimumValue)
                    ce.setMaximumValue(stats.maximumValue)
    
            if isinstance(r, QgsMultiBandColorRenderer):
                newRenderer = r.clone()
    
                ceR = getCE(r.redBand())
                ceG = getCE(r.greenBand())
                ceB = getCE(r.blueBand())
    
                newRenderer.setRedContrastEnhancement(ceR)
                newRenderer.setGreenContrastEnhancement(ceG)
                newRenderer.setBlueContrastEnhancement(ceB)
    
            elif isinstance(r, QgsSingleBandPseudoColorRenderer):
                newRenderer = r.clone()
                ce = getCE(newRenderer.band())
    
                # stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)
    
                shader = newRenderer.shader()
                newRenderer.setClassificationMax(ce.maximumValue())
                newRenderer.setClassificationMin(ce.minimumValue())
                shader.setMaximumValue(ce.maximumValue())
                shader.setMinimumValue(ce.minimumValue())
    
            elif isinstance(r, QgsSingleBandGrayRenderer):
    
                newRenderer = r.clone()
                ce = getCE(newRenderer.grayBand())
                newRenderer.setContrastEnhancement(ce)
    
            elif isinstance(r, QgsPalettedRasterRenderer):
    
                if isinstance(layer, SensorProxyLayer):
                    self.mMapView.sensorProxyLayer(layer.sensor()).setRenderer(newRenderer)
                elif isinstance(layer, QgsRasterLayer):
                    layer.setRenderer(layer)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Opens a dialog to save the map as local file
            :param fileType:
            :return:
            """
    
            import eotimeseriesviewer.settings
            lastDir = eotimeseriesviewer.settings.value(eotimeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.expanduser('~'))
            from eotimeseriesviewer.utils import filenameFromString
            from eotimeseriesviewer.mapvisualization import MapView
    
            if isinstance(self.mTSD, TimeSeriesDate) and isinstance(self.mMapView, MapView):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                path = filenameFromString('{}.{}'.format(self.mTSD.date(), self.mMapView.title()))
    
            else:
                path = 'mapcanvas'
            path = jp(lastDir, '{}.{}'.format(path, fileType.lower()))
    
            path, _ = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
    
            if len(path) > 0:
                self.saveAsImage(path, None, fileType)
    
                eotimeseriesviewer.settings.setValue(eotimeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.dirname(path))
    
        def setSpatialExtent(self, spatialExtent: SpatialExtent):
    
            """
            Sets the SpatialExtent to be shown.
            :param spatialExtent: SpatialExtent
            """
    
            assert isinstance(spatialExtent, SpatialExtent)
    
            if self.spatialExtent() != spatialExtent:
    
                spatialExtent = spatialExtent.toCrs(self.crs())
    
                if isinstance(spatialExtent, SpatialExtent):
                    self.setExtent(spatialExtent)
    
        def setSpatialCenter(self, spatialPoint: SpatialPoint):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Sets the map center
            :param spatialPoint: SpatialPoint
            """
            center = spatialPoint.toCrs(self.crs())
    
            if isinstance(center, SpatialPoint):
                self.setCenter(center)
    
        def spatialExtent(self)->SpatialExtent:
            """
            Returns the map extent as SpatialExtent (extent + CRS)
            :return: SpatialExtent
            """
            return SpatialExtent.fromMapCanvas(self)
    
        def spatialCenter(self)->SpatialPoint:
            """
            Returns the map center as SpatialPoint (QgsPointXY + CRS)
            :return: SpatialPoint
            """
            return SpatialPoint.fromMapCanvasCenter(self)
    
        def spatialExtentHint(self)->SpatialExtent:
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns a hint for a SpatialExtent, derived from the first raster layer
            :return: SpatialExtent
    
            crs = self.crs()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            layers = self.layers()
            if len(layers) > 0:
                e = self.fullExtent()
                ext = SpatialExtent(crs, e)
            else:
                ext = SpatialExtent.world()
    
    
    
    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())
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            geom = QgsPolygon()
    
            assert geom.fromWkt(ext.asWktPolygon())
    
            self.mCanvasExtents[canvas] = (ext, geom)
            self.refreshExtents()
    
        def refreshExtents(self):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            multi = QgsPolygon()
    
            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