Newer
Older
# -*- coding: utf-8 -*-
"""
/***************************************************************************

Benjamin Jakimow
committed
EO Time Series Viewer
-------------------
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
import sys
import typing
import os
import re
import enum
from qgis.PyQt.QtGui import QIcon, QContextMenuEvent, QMouseEvent, QPainter, QFont, QColor
from qgis.PyQt.QtWidgets import QApplication, QDialog, QMenu, QFileDialog, QSizePolicy, QStyle, QStyleOptionProgressBar
from qgis.PyQt.QtCore import QSize, QDate, QDateTime, QDir, QFile, QMimeData, pyqtSignal, Qt, \
QPoint, QObject, QRectF, QPointF, QRect, QTimer
from qgis.core import QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsContrastEnhancement, \
QgsDateTimeRange, QgsProject, QgsTextRenderer, QgsApplication, QgsCoordinateReferenceSystem, \
QgsMapToPixel, QgsRenderContext, QgsMapSettings, QgsRasterRenderer, \
QgsRasterBandStats, QgsPalettedRasterRenderer, QgsPointXY, \
QgsSingleBandPseudoColorRenderer, QgsWkbTypes, QgsRasterLayerTemporalProperties, QgsRasterDataProvider, \
QgsTextFormat, QgsMapLayerStore, QgsMultiBandColorRenderer, QgsSingleBandGrayRenderer, QgsField, \
QgsRectangle, QgsPolygon, QgsMultiBandColorRenderer, QgsRectangle, QgsSingleBandGrayRenderer, \
QgsLayerTreeGroup, QgsUnitTypes, QgsMimeDataUtils
from qgis.gui import QgsMapCanvas, QgisInterface, QgsFloatingWidget, QgsUserInputWidget, \
QgsMapTool, QgsMapToolPan, QgsMapToolZoom, QgsMapToolCapture, QgsMapToolIdentify, \
QgsGeometryRubberBand
from .externals.qps.classification.classificationscheme import ClassificationScheme, ClassInfo
from .externals.qps.crosshair.crosshair import CrosshairDialog, CrosshairStyle, CrosshairMapCanvasItem
from .externals.qps.layerproperties import showLayerPropertiesDialog
from .externals.qps.maptools import QgsMapToolSelectionHandler, \
CursorLocationMapTool, QgsMapToolAddFeature, \
SpectralProfileMapTool, TemporalProfileMapTool, MapToolCenter, PixelScaleExtentMapTool, FullExtentMapTool, QgsMapToolSelect
from .externals.qps.utils import SpatialExtent, SpatialPoint
from .labeling import quickLabelLayers, setQuickTSDLabelsForRegisteredLayers
from .timeseries import TimeSeriesDate, TimeSeriesSource, SensorProxyLayer
import eotimeseriesviewer.settings
KEY_LAST_CLICKED = 'LAST_CLICKED'
uri = QgsMimeDataUtils.Uri()
uri.name = mapLayer.name()
uri.providerKey = mapLayer.dataProvider().name()
uri.uri = mapLayer.source()
if isinstance(mapLayer, QgsRasterLayer):
uri.layerType = 'raster'
elif isinstance(mapLayer, QgsVectorLayer):
uri.layerType = 'vector'
else:
raise NotImplementedError()
return uri
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(MapLoadingInfoItem, self).__init__(mapCanvas)
self.mCanvas = mapCanvas
self.mProgressConnection = None
self.mCanvas.renderStarting.connect(lambda: self.showLoadingProgress(True))
# self.mCanvas.renderComplete.connect(lambda: self.showLoadingProgress(False))
MapLoadingInfoItem.PROGRESS_TIMER.timeout.connect(self.onProgressTimeOut)
self.mShowProgress = False
self.mIsVisible = True
def showLoadingProgress(self, showProgress: bool):
self.mShowProgress = showProgress
self.update()
def onProgressTimeOut(self):
if self.mShowProgress:
self.mCanvas.update()
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
Paints the crosshair
:param painter:
:param QStyleOptionGraphicsItem:
:param QWidget_widget:
:return:
"""
if False and self.mShowProgress:
if True:
options = QStyleOptionProgressBar()
options.rect = QRect(0, 0, painter.window().width(), 25)
options.textAlignment = Qt.AlignCenter
options.progress = 0
options.maximum = 0
options.minimum = 0
QApplication.style().drawControl(QStyle.CE_ProgressBar, options, painter)
class MapCanvasInfoItem(QgsMapCanvasItem):

Benjamin Jakimow
committed
"""
A QgsMapCanvasItem to show text
"""
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(MapCanvasInfoItem, self).__init__(mapCanvas)
self.mCanvas = mapCanvas

Benjamin Jakimow
committed
self.mText = dict()
self.mWrapChar = '\n'
self.mTextFormat = QgsTextFormat()
self.mTextFormat.setSizeUnit(QgsUnitTypes.RenderPixels)
self.mTextFormat.setFont(QFont('Helvetica', pointSize=10))
self.mTextFormat.setColor(QColor('yellow'))

Benjamin Jakimow
committed
"""
Sets a Wrap Character
:param c:
:return:
"""
self.mWrapChar = c
return self.wrapChar()

Benjamin Jakimow
committed
return self.mWrapChar
def setText(self, text: str, alignment: Qt.Alignment = Qt.AlignTop | Qt.AlignHCenter):

Benjamin Jakimow
committed
self.mText[alignment] = text

Benjamin Jakimow
committed
assert isinstance(format, QgsTextFormat)
self.mTextFormat = format
self.updateCanvas()

Benjamin Jakimow
committed
"""
Returns the text format.
:return: QgsTextFormat
"""
return self.mTextFormat

Benjamin Jakimow
committed
"""
Returns the font used to write text on the map canvas.
:return: QFont
"""
return self.mTextFormat.font()

Benjamin Jakimow
committed
self.mTextFormat.setFont(font)
"""
Sets the map info color
:param color: QColor
"""

Benjamin Jakimow
committed
self.mTextFormat.setColor(color)
"""
Returns the info text color
:return: QColor
"""

Benjamin Jakimow
committed
return self.mTextFormat.color()
def paintText(self, painter, text: str, flags, rotation=0):

Benjamin Jakimow
committed
padding = 5
text = text.replace('\\n', '\n')
text = text.split(self.wrapChar())

Benjamin Jakimow
committed
nl = len(text)

Benjamin Jakimow
committed
r = QgsTextRenderer()

Benjamin Jakimow
committed
painter.setBrush(Qt.NoBrush)
painter.setPen(Qt.NoPen)
painter.setRenderHint(QPainter.Antialiasing)

Benjamin Jakimow
committed
context = QgsRenderContext()

Benjamin Jakimow
committed
# taken from QGIS Repo src/core/qgspallabeling.cpp
m2p = QgsMapToPixel(1, 0, 0, 0, 0, 0)
context.setMapToPixel(m2p)
context.setScaleFactor(QgsApplication.desktop().logicalDpiX() / 25.4)
context.setUseAdvancedEffects(True)
context.setPainter(painter)
# context.setExtent(self.mCanvas.extent())
# context.setExpressionContext(self.mCanvas.mapSettings().expressionContext())

Benjamin Jakimow
committed
vp = QRectF(painter.viewport())

Benjamin Jakimow
committed
textFormat = self.mTextFormat
assert isinstance(textFormat, QgsTextFormat)
th = r.textHeight(context, textFormat, text, QgsTextRenderer.Rect)

Benjamin Jakimow
committed
tw = r.textWidth(context, textFormat, text)

Benjamin Jakimow
committed
# area to place the text inside
rect = QRectF()

Benjamin Jakimow
committed
hAlign = QgsTextRenderer.AlignCenter
# horizontal position
if bool(flags & Qt.AlignLeft):
x = padding
hAlign = QgsTextRenderer.AlignLeft
elif bool(flags & Qt.AlignHCenter):
x = 0.5 * vp.width()
hAlign = QgsTextRenderer.AlignCenter
elif bool(flags & Qt.AlignRight):
x = vp.width() - padding
hAlign = QgsTextRenderer.AlignRight
# vertical position
if bool(flags & Qt.AlignTop):

Benjamin Jakimow
committed
elif bool(flags & Qt.AlignVCenter):
y = 0.5 * (vp.height() + th)
elif bool(flags & Qt.AlignBottom):

Benjamin Jakimow
committed
poo = QPointF(x, y)
r.drawText(poo, rotation, hAlign, text, context, textFormat)

Benjamin Jakimow
committed
self.setText(text, Qt.AlignTop | Qt.AlignLeft)
def setMiddleLeft(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignLeft)
def setLowerLeft(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignLeft)

Benjamin Jakimow
committed
self.setText(text, Qt.AlignTop | Qt.AlignHCenter)
def setMiddleCenter(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignHCenter)
def setLowerCenter(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignHCenter)

Benjamin Jakimow
committed
self.setText(text, Qt.AlignTop | Qt.AlignRight)
def setMiddleRight(self, text: str):
self.setText(text, Qt.AlignVCenter | Qt.AlignRight)
def setLowerRight(self, text: str):
self.setText(text, Qt.AlignBottom | Qt.AlignRight)
def clearText(self):
self.mText.clear()
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
Paints the crosshair
:param painter:
:param QStyleOptionGraphicsItem:
:param QWidget_widget:
:return:
"""
for alignment, text in self.mText.items():
if isinstance(text, str) and len(text) > 0:
self.paintText(painter, text, alignment)
def __init__(self, canvas: QgsMapCanvas, cadDock: QgsAdvancedDigitizingDockWidget):
super(MapCanvasMapTools, self).__init__(canvas)
self.mCanvas = canvas
self.mCadDock = cadDock
self.mtZoomIn = QgsMapToolZoom(canvas, False)
self.mtZoomOut = QgsMapToolZoom(canvas, True)
self.mtMoveToCenter = MapToolCenter(canvas)
self.mtPan = QgsMapToolPan(canvas)
self.mtPixelScaleExtent = PixelScaleExtentMapTool(canvas)
self.mtFullExtentMapTool = FullExtentMapTool(canvas)
self.mtCursorLocation = CursorLocationMapTool(canvas, True)

Benjamin Jakimow
committed
self.mtAddFeature = QgsMapToolAddFeature(canvas, QgsMapToolCapture.CaptureNone, cadDock)
self.mtSelectFeature = QgsMapToolSelect(canvas)
from .externals.qps.maptools import MapTools
if mapToolKey == MapTools.ZoomIn:
self.mCanvas.setMapTool(self.mtZoomIn)
elif mapToolKey == MapTools.ZoomOut:
self.mCanvas.setMapTool(self.mtZoomOut)
elif mapToolKey == MapTools.Pan:
self.mCanvas.setMapTool(self.mtPan)
elif mapToolKey == MapTools.ZoomFull:
self.mCanvas.setMapTool(self.mtFullExtentMapTool)
elif mapToolKey == MapTools.ZoomPixelScale:
self.mCanvas.setMapTool(self.mtPixelScaleExtent)
elif mapToolKey == MapTools.CursorLocation:
self.mCanvas.setMapTool(self.mtCursorLocation)
elif mapToolKey == MapTools.SpectralProfile:
pass
elif mapToolKey == MapTools.TemporalProfile:
pass
elif mapToolKey == MapTools.MoveToCenter:
self.mCanvas.setMapTool(self.mtMoveToCenter)
elif mapToolKey == MapTools.AddFeature:
self.mCanvas.setMapTool(self.mtAddFeature)
elif mapToolKey == MapTools.SelectFeature:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectSimple)
elif mapToolKey == MapTools.SelectFeatureByPolygon:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectPolygon)
elif mapToolKey == MapTools.SelectFeatureByFreehand:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectFreehand)
elif mapToolKey == MapTools.SelectFeatureByRadius:
self.mCanvas.setMapTool(self.mtSelectFeature)
self.mtSelectFeature.setSelectionMode(QgsMapToolSelectionHandler.SelectionMode.SelectRadius)
else:
print('Unknown MapTool key: {}'.format(mapToolKey))
# if undefined, set a current vector layer
if mapToolKey in [MapTools.SelectFeature, MapTools.SelectFeatureByPolygon, MapTools.SelectFeatureByRadius,
MapTools.SelectFeatureByFreehand] \
and self.mCanvas.currentLayer() is None:
for vl in self.mCanvas.layers():
if isinstance(vl, QgsVectorLayer):
self.mCanvas.setCurrentLayer(vl)
break
class MapCanvas(QgsMapCanvas):
"""
A widget based on QgsMapCanvas to draw spatial data
"""
"""
Canvas specific commands
"""
UpdateMapItems = 5
saveFileDirectories = dict()
sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
# sigChangeDVRequest = pyqtSignal(QgsMapCanvas, str)
# sigChangeMVRequest = pyqtSignal(QgsMapCanvas, str)
# sigChangeSVRequest = pyqtSignal(QgsMapCanvas, QgsRasterRenderer)

Benjamin Jakimow
committed
sigCrosshairPositionChanged = pyqtSignal(SpatialPoint)
sigCrosshairVisibilityChanged = pyqtSignal(bool)
sigDestinationCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem)

Benjamin Jakimow
committed
sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
def __init__(self, parent=None):
super(MapCanvas, self).__init__(parent=parent)
self.mMapLayerStore: QgsProject = QgsProject.instance()
self.mMapTools = None
self.initMapTools()

benjamin.jakimow@geo.hu-berlin.de
committed
self.mCrosshairItem = CrosshairMapCanvasItem(self)
self.mInfoItem = MapCanvasInfoItem(self)
self.mProgressItem = MapLoadingInfoItem(self)
self.mUserInputWidget = QgsUserInputWidget(self)
self.mUserInputWidget.setObjectName('UserInputDockWidget')
self.mUserInputWidget.setAnchorWidget(self)
self.mUserInputWidget.setAnchorWidgetPoint(QgsFloatingWidget.TopRight)
self.mUserInputWidget.setAnchorPoint(QgsFloatingWidget.TopRight)
# self.mProgressBar = QProgressBar()
# self.mUserInputWidget.addUserInputWidget(self.mProgressBar)
self.mNeedsRefresh = False
self.mRenderingFinished = True
self.mIsRefreshing = False
t2 = time.time()
dt = t2 - self.mRefreshStartTime
self.sigMapRefreshed[float].emit(dt)
self.sigMapRefreshed[float, float].emit(self.mRefreshStartTime, t2)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
bg = eotimeseriesviewer.settings.value(
eotimeseriesviewer.settings.Keys.MapBackgroundColor, default=QColor(0, 0, 0))
self.setContextMenuPolicy(Qt.DefaultContextMenu)
def onExtentsChanged(self):
self.sigSpatialExtentChanged.emit(self.spatialExtent())
# self.destinationCrsChanged.connect(lambda: self.sigDestinationCrsChanged.emit(self.crs()))
"""
Returns the mapcanvas QgsUserInputWidget
:return: QgsUserInputWidget
"""
return self.mUserInputWidget
"""
Returns the MapCanvasInfoItem, e.g. to plot text on top of the map canvas
:return: MapCanvasInfoItem
"""
return self.mInfoItem
"""
Returns the MapView this MapCanvas is linked to
:return:
"""
return self.mMapView
"""
Returns the map tools of this MapCanvas
:return: MapCanvasMapTools
"""
return self.mMapTools
def initMapTools(self):
self.mCadDock = QgsAdvancedDigitizingDockWidget(self)
self.mCadDock.setVisible(False)
self.mMapTools = MapCanvasMapTools(self, self.mCadDock)
def setMapLayerStore(self, store: typing.Union[QgsMapLayerStore, QgsProject]):
"""
Sets the QgsMapLayerStore or QgsProject instance that is used to register map layers
:param store: QgsMapLayerStore | QgsProject
"""
assert isinstance(store, (QgsMapLayerStore, QgsProject))
self.mMapLayerStore = store
"""
Returns whether the MapCanvas is processing a rendering task
:return: bool
"""
return self.mRenderingFinished

Benjamin Jakimow
committed

Benjamin Jakimow
committed
b = event.button() == Qt.LeftButton
if b and isinstance(self.mapTool(), QgsMapTool):
b = isinstance(self.mapTool(), (QgsMapToolIdentify,
CursorLocationMapTool,
SpectralProfileMapTool, TemporalProfileMapTool))
super(MapCanvas, self).mousePressEvent(event)
if b:
ms = self.mapSettings()
pointXY = ms.mapToPixel().toMapCoordinates(event.x(), event.y())
spatialPoint = SpatialPoint(ms.destinationCrs(), pointXY)
self.setCrosshairPosition(spatialPoint)
"""
Sets the map canvas MapView
:param mapView: MapView
"""
from eotimeseriesviewer.mapvisualization import MapView

Benjamin Jakimow
committed
self.mInfoItem.setTextFormat(mapView.mapTextFormat())
self.addToRefreshPipeLine(mapView.mapBackgroundColor())
self.setCrosshairStyle(mapView.crosshairStyle())
#self.setCrosshairVisibility(mapView.crosshairStyle())
# self.addToRefreshPipeLine(MapCanvas.Command.UpdateMapItems)
Sets the TimeSeriesDate this map-canvas is linked to
if self.mTSD == tsd:
return
# disconnect old TSD
if isinstance(self.mTSD, TimeSeriesDate):
self.mTSD.sensor().sigNameChanged.disconnect(self.updateScope)
self.mTSD = tsd
self.mTSD.sensor().sigNameChanged.connect(self.updateScope)
self.updateScope()
def updateScope(self):
"""
Updates map-canvas TSD variables
"""
varMVNumber = None
varMVName = None
varDate = None
varDOY = None
varSensor = None
tsd = self.tsd()
if isinstance(tsd, TimeSeriesDate):
varDate = str(tsd.date())
varDOY = tsd.doy()
varSensor = tsd.sensor().name()
mv = self.mapView()
if isinstance(mv, MapView):
varMVName = mv.name()
if isinstance(EOTimeSeriesViewer.instance(), EOTimeSeriesViewer):
mapViews = EOTimeSeriesViewer.instance().mapViews()
if mv in mapViews:
varMVNumber = mapViews.index(mv) + 1
scope = self.expressionContextScope()
scope.setVariable('map_view_num', varMVNumber, isStatic=False)
scope.setVariable('map_view', varMVName, isStatic=False)
scope.setVariable('map_date', varDate, isStatic=False)
scope.setVariable('map_doy', varDOY, isStatic=False)
scope.setVariable('map_sensor', varSensor, isStatic=False)
Returns the TimeSeriesDate
:return: TimeSeriesDate

Benjamin Jakimow
committed
"""
Sets the spatial extent
:param extent: SpatialExtent
"""
assert isinstance(extent, SpatialExtent)
extent = extent.toCrs(self.crs())
self.setExtent(extent)
"""
Sets the SpatialCenter
:param center: SpatialPoint
"""
assert isinstance(center, SpatialPoint)
center = center.toCrs(self.crs())
self.setCenter(center)
"""
Changes the map-canvas size
:param size: QSize
"""
assert isinstance(size, QSize)
if self.size() != size:
super(MapCanvas, self).setFixedSize(size)
def setCrs(self, crs: QgsCoordinateReferenceSystem):
if self.crs() != crs:
self.setDestinationCrs(crs)
"""
Shortcut to return self.mapSettings().destinationCrs()
:return: QgsCoordinateReferenceSystem
"""
return self.mapSettings().destinationCrs()
def setLayers(self, mapLayers):
"""
Set the map layers and, if necessary, registers the in a QgsMapLayerStore
:param mapLayers:
"""
self.mMapLayerStore.addMapLayers(mapLayers)
super(MapCanvas, self).setLayers(mapLayers)
"""
Returns whether the MapCanvas is visible to a user and not hidden behind the invisible regions of a scroll area.
:return: bool
"""
return self.visibleRegion().boundingRect().isValid()
def addToRefreshPipeLine(self, arguments: list):
"""
Adds commands or other arguments to a pipeline which will be handled during the next timed refresh.
:param arguments: argument | [list-of-arguments]
"""
if not isinstance(arguments, list):
arguments = [arguments]
if isinstance(a, SpatialExtent):
self.mTimedRefreshPipeLine[SpatialPoint] = a
elif isinstance(a, QColor):
self.mTimedRefreshPipeLine[QColor] = a
elif isinstance(a, MapCanvas.Command):
if not MapCanvas.Command in self.mTimedRefreshPipeLine.keys():
self.mTimedRefreshPipeLine[MapCanvas.Command] = []
# remove previous commands of same type, append command to end
while a in self.mTimedRefreshPipeLine[MapCanvas.Command]:
self.mTimedRefreshPipeLine[MapCanvas.Command].remove(a)
self.mTimedRefreshPipeLine[MapCanvas.Command].append(a)
print('Unsupported argument: {} {}'.format(type(a), str(a)), file=sys.stderr)
def timedRefresh(self):
"""
Called to refresh the map canvas with all things needed to be done with lazy evaluation
existingSources = [l.source() for l in existing]
from .mapvisualization import MapView
from .timeseries import SensorInstrument
mapView: MapView = self.mapView()
self.setLayers([])
self.mInfoItem.clearText()
self.update()
return
sensor: SensorInstrument = self.tsd().sensor()
for lyr in mapView.visibleLayers():
assert isinstance(lyr, QgsMapLayer)
if isinstance(lyr, SensorProxyLayer):
if sensor == lyr.sensor():
# check if we need to add a new source
for tss in self.tsd():
if not tss.isVisible():
continue
if source in existingSources:
sourceLayer = existing[existingSources.index(source)]
else:
loadDefaultStyle = mapView.mLayerStyleInitialized.get(sensor, False) == False
master: SensorProxyLayer = mapView.sensorProxyLayer(sensor)
sourceLayer = SensorProxyLayer(source,
sensor=sensor,
options=QgsRasterLayer.LayerOptions(
loadDefaultStyle=loadDefaultStyle))
sourceLayer.setName(f'{lyr.name()} {source}')
sourceLayer.setCustomProperty('eotsv/sensorid', sensor.id())
sourceLayer.setMapLayerStyle(master.mapLayerStyle())
sourceLayer.styleChanged.connect(lambda *args, l=sourceLayer: self.onSetMasterLayerStyle(l))
assert isinstance(sourceLayer, QgsRasterLayer)
expected.append(sourceLayer)
else:
# skip any other SensorProxyLayer that relates to another sensor
pass
else:
expected.append(lyr)
if len(self.mTimedRefreshPipeLine) == 0 and self.layers() == expected:
# there is nothing to do.
if True:
# set sources first
keys = self.mTimedRefreshPipeLine.keys()
if QgsCoordinateReferenceSystem in keys:
self.setDestinationCrs(self.mTimedRefreshPipeLine.pop(QgsCoordinateReferenceSystem))
if SpatialExtent in keys:
self.setSpatialExtent(self.mTimedRefreshPipeLine.pop(SpatialExtent))
if SpatialPoint in keys:
self.setSpatialCenter(self.mTimedRefreshPipeLine.pop(SpatialPoint))
self.setCanvasColor(self.mTimedRefreshPipeLine.pop(QColor))
if lyrs != expected:
self.setLayers(expected)
if MapCanvas.Command in keys:
commands = self.mTimedRefreshPipeLine.pop(MapCanvas.Command)
for command in commands:
assert isinstance(command, MapCanvas.Command)
if command == MapCanvas.Command.RefreshRenderer:
# master = self.mapView().sensorProxyLayer(sensor)
# masterStyle = QgsMapLayerStyle()
# masterStyle.readFromLayer(master)
# masterStyleXML = masterStyle.xmlData()
for l in self.layers():
if isinstance(l, SensorProxyLayer) and l.sensor() == sensor:
l.triggerRepaint()
# style = QgsMapLayerStyle()
# style.readFromLayer(l)
# if style.xmlData() == masterStyleXML:
self.mTimedRefreshPipeLine.clear()
def onSetMasterLayerStyle(self, lyr: SensorProxyLayer):
if isinstance(lyr, SensorProxyLayer):
master: SensorProxyLayer = self.mapView().sensorProxyLayer(lyr.sensor())
if isinstance(master, SensorProxyLayer):
master.setMapLayerStyle(lyr.mapLayerStyle())
def setLayerVisibility(self, cls, isVisible: bool):
"""
:param cls: type of layer, e.g. QgsRasterLayer to set visibility of all layers of same type
QgsMapLayer instance to the visibility of a specific layer
:param isVisible: bool
"""
self.mMapLayerModel.setLayerVisibility(cls, isVisible)
self.addToRefreshPipeLine(MapCanvas.Command.RefreshVisibility)
def setCrosshairStyle(self, crosshairStyle: CrosshairStyle, emitSignal=True):
"""
Sets the CrosshairStyle
:param crosshairStyle: CrosshairStyle
:param emitSignal: Set to Fals to no emit a signal.
"""
from eotimeseriesviewer import CrosshairStyle
if crosshairStyle is None:

Benjamin Jakimow
committed
self.mCrosshairItem.crosshairStyle.setShow(False)
self.mCrosshairItem.update()
else:
assert isinstance(crosshairStyle, CrosshairStyle)

Benjamin Jakimow
committed
self.mCrosshairItem.setCrosshairStyle(crosshairStyle)

Benjamin Jakimow
committed
if emitSignal:
s = ""
#self.sigCrosshairStyleChanged.emit(self.mCrosshairItem.crosshairStyle())
else:
s = ""

Benjamin Jakimow
committed
"""
Returns the style of the Crosshair.
:return: CrosshairStyle
"""

Benjamin Jakimow
committed
def setCrosshairPosition(self, spatialPoint: SpatialPoint):

Benjamin Jakimow
committed
"""
Sets the position of the Crosshair.
:param spatialPoint: SpatialPoint
:param emitSignal: True (default). Set False to avoid emitting sigCrosshairPositionChanged
:return:
"""

Benjamin Jakimow
committed
point = spatialPoint.toCrs(self.mapSettings().destinationCrs())
if self.mCrosshairItem.mPosition != point:
if self.mCrosshairItem.visibility() == False:
s = ""
else:
s = ""
self.mCrosshairItem.setPosition(point)

Benjamin Jakimow
committed
self.sigCrosshairPositionChanged.emit(point)

Benjamin Jakimow
committed
"""Returns the last crosshair position"""
return self.mCrosshairItem.mPosition
def setCrosshairVisibility(self, b: bool, emitSignal=True):

Benjamin Jakimow
committed
"""

Benjamin Jakimow
committed
:param b: bool
"""
if b and self.mCrosshairItem.mPosition is None:
self.mCrosshairItem.setPosition(self.spatialCenter())
self.sigCrosshairPositionChanged.emit(self.spatialCenter())
if b != self.mCrosshairItem.visibility():
self.mCrosshairItem.setVisibility(b)
if emitSignal:
self.sigCrosshairVisibilityChanged.emit(b)
def layerPaths(self):
:return: [list-of-str]

benjamin.jakimow@geo.hu-berlin.de
committed
return [str(l.source()) for l in self.layers()]
def pixmap(self):
"""
Returns the current map image as pixmap
return self.grab()
"""
Creates the MapCanvas context menu with options relevant for pixel position ``pos``.
:param pos: QPoint
:return: QMenu
"""
mapSettings = self.mapSettings()
assert isinstance(mapSettings, QgsMapSettings)
pointGeo = mapSettings.mapToPixel().toMapCoordinates(pos.x(), pos.y())
assert isinstance(pointGeo, QgsPointXY)
from .main import EOTimeSeriesViewer
eotsv = EOTimeSeriesViewer.instance()

Benjamin Jakimow
committed
viewPortMapLayers = [l for l in self.layers() if isinstance(l, QgsMapLayer)]

Benjamin Jakimow
committed
viewPortRasterLayers = [l for l in viewPortMapLayers if isinstance(l, QgsRasterLayer) and
SpatialExtent.fromLayer(l).toCrs(self.crs()).contains(pointGeo)]

Benjamin Jakimow
committed
viewPortSensorLayers = [l for l in viewPortRasterLayers if isinstance(l, SensorProxyLayer)]
viewPortVectorLayers = [l for l in viewPortMapLayers if isinstance(l, QgsVectorLayer)]

Benjamin Jakimow
committed
refSensorLayer = None
refRasterLayer = None
if len(viewPortRasterLayers) > 0:
refRasterLayer = viewPortRasterLayers[0]
if len(viewPortSensorLayers) > 0:
refSensorLayer = viewPortSensorLayers[0]
menu = QMenu()
if isinstance(self.tsd(), TimeSeriesDate):

Benjamin Jakimow
committed
tss = None
sourceUris = self.tsd().sourceUris()
for sl in viewPortSensorLayers:
if sl.source() in sourceUris:
tss = self.tsd()[sourceUris.index(sl.source())]
break
lyrWithSelectedFeatures = [l for l in quickLabelLayers() if l.selectedFeatureCount() > 0]
layerNames = ', '.join([l.name() for l in lyrWithSelectedFeatures])
nQuickLabelLayers = len(lyrWithSelectedFeatures)
m.setEnabled(nQuickLabelLayers > 0)
a.setToolTip('Writes the dates and sensor quick labels of selected features in {}.'.format(layerNames))
a.triggered.connect(lambda *args,
tsd=self.tsd(),
tss=tss: setQuickTSDLabelsForRegisteredLayers(tsd, tss))
from .labeling import layerClassSchemes, setQuickClassInfo
if len(lyrWithSelectedFeatures) == 0:
a = m.addAction('No features selected.')
a.setToolTip('Select feature in the labeling panel to apply Quick label value on.')
a.setEnabled(False)
else:
for layer in lyrWithSelectedFeatures:
assert isinstance(layer, QgsVectorLayer)
csf = layerClassSchemes(layer)
if len(csf) > 0:
m.addSection(layer.name())
for (cs, field) in csf:
assert isinstance(cs, ClassificationScheme)
assert isinstance(field, QgsField)
classMenu = m.addMenu('"{}" ({})'.format(field.name(), field.typeName()))
for classInfo in cs:
assert isinstance(classInfo, ClassInfo)
a = classMenu.addAction('{} "{}"'.format(classInfo.label(), classInfo.name()))
a.setIcon(classInfo.icon())
a.triggered.connect(
lambda _, vl=layer, f=field, c=classInfo: setQuickClassInfo(vl, f, c))

Benjamin Jakimow
committed
if isinstance(refSensorLayer, SensorProxyLayer):
m = menu.addMenu('Raster stretch...')
action = m.addAction('Linear')
action.triggered.connect(lambda *args, lyr=refSensorLayer:
self.stretchToExtent(self.spatialExtent(), 'linear_minmax', layer=lyr, p=0.0))

Benjamin Jakimow
committed
action = m.addAction('Linear 5%')
action.triggered.connect(lambda *args, lyr=refSensorLayer:
self.stretchToExtent(self.spatialExtent(), 'linear_minmax', layer=lyr, p=0.05))

Benjamin Jakimow
committed
action = m.addAction('Gaussian')
action.triggered.connect(lambda *args, lyr=refSensorLayer:
self.stretchToExtent(self.spatialExtent(), 'gaussian', layer=lyr, n=3))

Benjamin Jakimow
committed
menu.addSeparator()
from .externals.qps.layerproperties import pasteStyleToClipboard

Benjamin Jakimow
committed
b = isinstance(refRasterLayer, QgsRasterLayer)
a = menu.addAction('Copy Style')
a.setEnabled(b)
a.setToolTip('Copy the current layer style to clipboard')
a.triggered.connect(lambda *args, lyr=refRasterLayer: pasteStyleToClipboard(lyr))
a = menu.addAction('Paste Style')
a.setEnabled(False)
clipBoardMime = QApplication.clipboard().mimeData()
if isinstance(clipBoardMime, QMimeData) and 'application/qgis.style' in clipBoardMime.formats():
a.setEnabled(True)

Benjamin Jakimow
committed
a.triggered.connect(lambda *args, lyr=refRasterLayer: self.onPasteStyleFromClipboard(lyr))
menu.addSeparator()
m = menu.addMenu('Layers...')
visibleLayers = viewPortRasterLayers + viewPortVectorLayers