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
from qgis.core import *
from qgis.gui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtXml import QDomDocument

Benjamin Jakimow
committed
from .utils import *
from .timeseries import TimeSeriesDatum
from .crosshair import CrosshairDialog, CrosshairStyle
from .maptools import *

Benjamin Jakimow
committed
from .labeling import LabelAttributeTableModel
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
class MapCanvasLayerModel(QAbstractTableModel):
class LayerItem(object):
def __init__(self, src):
self.mLyr = None
self.mUri = None
self.mIsVisible = True
self.mExternalControl = False
if isinstance(src, QgsMimeDataUtils.Uri):
self.mUri = src
self.mExternalControl = False
else:
assert isinstance(src, QgsMapLayer)
self.mLyr = src
self.mUri = toQgsMimeDataUtilsUri(src)
self.mExternalControl = True
s = ""
assert isinstance(self.mUri, QgsMimeDataUtils.Uri)
def name(self)->str:
return self.mUri.name
def source(self)->str:
return self.mUri.uri
def layerType(self)->str:
return self.mUri.layerType
def isVisible(self)->bool:
return self.mIsVisible
def hasMapLayerInstance(self)->bool:
return isinstance(self.mLyr, QgsMapLayer)
"""
A model to create QgsMapLayer instances and control its visibility.
"""
def __init__(self, parent=None):
super(MapCanvasLayerModel, self).__init__()
self.cnName = 'Name'
self.cnUri = 'Uri'
self.cnLayerType = 'Type'
self.mColumnNames = [self.cnName, self.cnLayerType, self.cnUri]
self.mVectorsVisible = True
self.mRastersVisible = True
self.mDefaultRasterRenderer = None
self.mDefaultVectorRenderer = None
self.mItems = []
def __iter__(self):
return iter(self.mItems)
def __len__(self)->int:
return len(self.mItems)
def setDefaultRasterRenderer(self, renderer:QgsRasterRenderer):

Benjamin Jakimow
committed
if isinstance(renderer, QgsRasterRenderer):
self.mDefaultRasterRenderer = renderer

Benjamin Jakimow
committed
for item in self.mItems:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
if not item.mExternalControl and isinstance(item.mLyr, QgsRasterLayer):
item.mLyr.setRenderer(renderer.clone())
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
def setDefaultVectorRenderer(self, renderer:QgsFeatureRenderer):
assert isinstance(renderer, QgsFeatureRenderer)
self.mDefaultVectorRenderer = renderer
for item in self.mItems:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
if not item.mExternalControl and isinstance(item.mLyr, QgsVectorLayer):
item.mLyr.setRenderer(renderer.clone())
def setLayerVisibility(self, cls, b:bool):
assert isinstance(b, bool)
if isinstance(cls, int):
item = self.mItems[cls]
assert isinstance(item, MapCanvasLayerModel.LayerItem)
item.mIsVisible = b
elif cls == QgsRasterLayer:
self.mRastersVisible = b
for item in [i for i in self if i.layerType() == 'raster']:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
item.mIsVisible = b
elif cls == QgsVectorLayer:
self.mVectorsVisible = b
for item in [i for i in self if i.layerType() == 'vector']:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
item.mIsVisible = b
else:
raise NotImplementedError()
def clear(self):
"""
Removes all layers
"""
self.beginRemoveRows(QModelIndex(), 0, len(self)-1)
self.mItems.clear()
self.endRemoveRows()
def addMapLayerSources(self, src):
i = len(self.mItems)
self.insertMapLayerSources(i, src)
def insertMapLayerSources(self, index:int, mapLayerSources):
assert isinstance(mapLayerSources, (list, types.GeneratorType))
items = [MapCanvasLayerModel.LayerItem(src) for src in mapLayerSources]
self.beginInsertRows(QModelIndex(), index, index + len(items) - 1)
i = index
for item in items:
self.mItems.insert(i, item)
i += 1
self.endInsertRows()
def visibleLayers(self, sorted=True)->list:
"""
Returns the visible QgsMapLayer instances. Will create QgsMapLayer instances if necessary from uri's
:return: [list-of-QgsMapLayers]
"""
layers = []
for item in self.mItems:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
if not item.isVisible():
continue
if not item.hasMapLayerInstance():
item.mLyr = toMapLayer(item.mUri)
assert isinstance(item.mLyr, QgsMapLayer)
if isinstance(self.mDefaultRasterRenderer, QgsRasterRenderer) and isinstance(item.mLyr, QgsRasterLayer):
item.mLyr.setRenderer(self.mDefaultRasterRenderer.clone())
if isinstance(self.mDefaultVectorRenderer, QgsFeatureRenderer) and isinstance(item.mLyr, QgsVectorLayer):
item.mLyr.setRenderer(self.mDefaultRasterRenderer.clone())
layers.append(item.mLyr)
if sorted:
layers = [l for l in layers if isinstance(l, QgsVectorLayer)] + \
[l for l in layers if isinstance(l, QgsRasterLayer)]
return layers
def rasterSources(self)->list:
return [s for s in self if isinstance(s, MapCanvasLayerModel.LayerItem) and s.layerType() == 'raster']
def vectorSources(self)->list:
return [s for s in self if isinstance(s, MapCanvasLayerModel.LayerItem) and s.layerType() == 'vector']
def removeMapLayerSources(self, mapLayerSources):
assert isinstance(mapLayerSources, (list, types.GeneratorType))
toRemove = []
for src in mapLayerSources:
uri = None
if isinstance(src, MapCanvasLayerModel.LayerItem):
uri = src.mUri.uri
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
if isinstance(src, QgsRasterLayer):
uri = src.source()
elif isinstance(src, QgsMimeDataUtils.Uri):
uri = src.uri
elif isinstance(src, str):
uri = src
for item, item in enumerate(self.mItems):
assert isinstance(item, MapCanvasLayerModel.LayerItem)
if item.mUri.uri == uri:
toRemove.append(item)
for item in toRemove:
idx = self.item2index(item)
self.beginRemoveRows(QModelIndex(), idx.row(), idx.row())
self.mItems.remove(item)
self.endRemoveRows()
def item2index(self, item)->QModelIndex:
assert isinstance(item, MapCanvasLayerModel.LayerItem)
i = self.mItems.index(item)
return self.createIndex(i,0,object=self.mItems[i])
def rowCount(self, parent = QModelIndex())->int:
return len(self.mItems)
def columnCount(self, parent = QModelIndex())->int:
return len(self.mColumnNames)
def flags(self, index:QModelIndex):
if index.isValid():
columnName = self.mColumnNames[index.column()]
flags = Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
if columnName == self.cnName: #allow check state
flags = flags | Qt.ItemIsUserCheckable
return flags
#return item.qt_flags(index.column())
return None
def headerData(self, col, orientation, role):
if Qt is None:
return None
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.mColumnNames[col]
elif orientation == Qt.Vertical and role == Qt.DisplayRole:
return col
return None
def data(self, index, role = Qt.DisplayRole):
if role is None or not index.isValid():
return None
item = self.mItems[index.row()]
assert isinstance(item, MapCanvasLayerModel.LayerItem)
cn = self.mColumnNames[index.column()]
value = None
if role == Qt.DisplayRole or role == Qt.ToolTipRole or role == Qt.EditRole:
if cn == self.cnName:
value = item.name()
elif cn == self.cnUri:
value = item.mUri.uri
elif cn == self.cnLayerType:
value = item.mUri.layerType
elif role == Qt.CheckStateRole:
if cn == self.cnName:
value = Qt.Checked if item.isVisible() else Qt.Unchecked
return value
def setData(self, index, value, role=None):
if role is None or not index.isValid():
return None
cn = self.mColumnNames[index.column()]
item = self.mItems[index.row()]
assert isinstance(item, MapCanvasLayerModel.LayerItem)
changed = False
if role == Qt.CheckStateRole and cn == self.cnName:
item.mIsVisible = value == Qt.Checked
changed = True
if changed:
self.dataChanged.emit(index, index, [role])
return changed
return False
class MapCanvas(QgsMapCanvas):
class Command(enum.Enum):
RefreshRenderer = 1
RefreshVisibility = 2
Clear = 3
RemoveRasters = 4
HideRasters = 5
ShowRasters = 6
RemoveVectors = 7
HideVectors = 8
ShowVectors = 9
saveFileDirectories = dict()
sigShowProfiles = pyqtSignal(SpatialPoint, str)
sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
sigChangeDVRequest = pyqtSignal(QgsMapCanvas, str)
sigChangeMVRequest = pyqtSignal(QgsMapCanvas, str)
sigChangeSVRequest = pyqtSignal(QgsMapCanvas, QgsRasterRenderer)

Benjamin Jakimow
committed
sigCrosshairPositionChanged = pyqtSignal(SpatialPoint)
sigCrosshairVisibilityChanged = pyqtSignal(bool)
from .crosshair import CrosshairStyle
sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
def __init__(self, parent=None):
super(MapCanvas, self).__init__(parent=parent)
self.mMapLayerStore = QgsProject.instance()
self.mMapLayers = []
self.mMapLayerModel = MapCanvasLayerModel()
self.mTimedRefreshPipeLine = dict()

benjamin.jakimow@geo.hu-berlin.de
committed

Benjamin Jakimow
committed
self.mLabelingModel = None
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 = timeseriesviewer.settings.value(timeseriesviewer.settings.Keys.MapBackgroundColor, default=QColor(0, 0, 0))
self.setCanvasColor(bg)
self.setContextMenuPolicy(Qt.DefaultContextMenu)
#refreshTimer.timeout.connect(self.onTimerRefresh)
#self.extentsChanged.connect(lambda : self._setDataRefreshed())
self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
from timeseriesviewer.crosshair import CrosshairMapCanvasItem

Benjamin Jakimow
committed
self.mCrosshairItem = CrosshairMapCanvasItem(self)
def mapLayerModel(self)->MapCanvasLayerModel:
"""
Returns the MapCanvasLayerModel which controls the layer visibility
:return: MapCanvasLayerModel
"""
return self.mMapLayerModel
def setMapLayerStore(self, store):
"""
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
def renderingFinished(self)->bool:
"""
Returns whether the MapCanvas is processing a rendering task
:return: bool
"""
return self.mRenderingFinished

Benjamin Jakimow
committed
def mousePressEvent(self, event:QMouseEvent):
b = event.button() == Qt.LeftButton
if b and isinstance(self.mapTool(), QgsMapTool):
from timeseriesviewer.maptools import CursorLocationMapTool
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)
def setMapView(self, mapView):
from timeseriesviewer.mapvisualization import MapView
assert isinstance(mapView, MapView)
self.mMapView = mapView

Benjamin Jakimow
committed
def setLabelingModel(self, model):
assert isinstance(model, (LabelAttributeTableModel, None))
self.mLabelingModel = model
def setTSD(self, tsd:TimeSeriesDatum):
"""
Sets the TimeSeriesDatum this map-canvas is linked to
:param tsd:
:return:
"""
assert isinstance(tsd, TimeSeriesDatum)
self.mTSD = tsd

Benjamin Jakimow
committed
scope = self.expressionContextScope()
scope.setVariable('map_date', str(tsd.date()), isStatic=True)
scope.setVariable('map_doy', tsd.doy(), isStatic=True)
scope.setVariable('map_sensor', tsd.sensor().name(), isStatic=False)
tsd.sensor().sigNameChanged.connect(lambda name : scope.setVariable('map_sensor', name))
tsd.sigSourcesChanged.connect(self.resetRasterSources)
self.resetRasterSources()
self.mapLayerModel().setDefaultRasterRenderer(self.defaultRasterRenderer())
def resetRasterSources(self, *args):
tsd = self.tsd()
self.mapLayerModel().clear()
if isinstance(tsd, TimeSeriesDatum):
self.mapLayerModel().addMapLayerSources(tsd.qgsMimeDataUtilsUris())
def tsd(self)->TimeSeriesDatum:
"""
Returns the TimeSeriesDatum
:return: TimeSeriesDatum
"""
return self.mTSD

Benjamin Jakimow
committed
def setSpatialExtent(self, extent:SpatialExtent):
"""
Sets the spatial extent
:param extent: SpatialExtent
"""
assert isinstance(extent, SpatialExtent)
extent = extent.toCrs(self.crs())
self.setExtent(extent)
def setSpatialCenter(self, center:SpatialPoint):
"""
Sets the SpatialCenter
:param center: SpatialPoint
"""
assert isinstance(center, SpatialPoint)
center = center.toCrs(self.crs())
self.setCenter(center)
def setFixedSize(self, size:QSize):
"""
Changes the map-canvas size
:param size: QSize
"""
assert isinstance(size, QSize)
if self.size() != size:
super(MapCanvas, self).setFixedSize(size)
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)
def isVisibleToViewport(self)->bool:
"""
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()
:return: [list-of-QgsMapLayers]
"""
return self.mMapLayerModel.visibleLayers(sorted=sorted)
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]
"""
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
if not isinstance(arguments, list):
arguments = [arguments]
for a in arguments:
if isinstance(a, (QgsMimeDataUtils.Uri, QgsMapLayer)):
if not 'sources' in self.mTimedRefreshPipeLine.keys():
self.mTimedRefreshPipeLine['sources'] = []
self.mTimedRefreshPipeLine['sources'].append(a)
elif isinstance(a, QgsRasterRenderer):
self.mTimedRefreshPipeLine[QgsFeatureRenderer] = a
elif isinstance(a, QgsFeatureRenderer):
self.mTimedRefreshPipeLine[QgsFeatureRenderer] = a
elif isinstance(a, SpatialExtent):
self.mTimedRefreshPipeLine[SpatialExtent] = a
elif isinstance(a, SpatialPoint):
self.mTimedRefreshPipeLine[SpatialExtent] = a
elif isinstance(a, MapCanvas.Command):
if not MapCanvas.Command in self.mTimedRefreshPipeLine.keys():
self.mTimedRefreshPipeLine[MapCanvas.Command] = []
#append command, remove previous of same type
while a in self.mTimedRefreshPipeLine[MapCanvas.Command]:
self.mTimedRefreshPipeLine[MapCanvas.Command].remove(a)
self.mTimedRefreshPipeLine[MapCanvas.Command].append(a)
else:
raise NotImplementedError('Unsupported argument: {}'.format(str(a)))
def timedRefresh(self):
"""
Called to refresh the map canvas with all things needed to be done with lazy evaluation
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
if len(self.mTimedRefreshPipeLine) == 0 and self.layers() == self.mapLayerModel().visibleLayers():
#there is nothing to do.
return
else:
self.freeze(True)
#look for new layers
mlm = self.mapLayerModel()
assert isinstance(mlm, MapCanvasLayerModel)
#set sources first
keys = self.mTimedRefreshPipeLine.keys()
if 'sources' in keys:
mlm.addMapLayerSources(self.mTimedRefreshPipeLine['sources'])
#set renderers
if QgsRasterRenderer in keys:
self.mapLayerModel().setDefaultRasterRenderer(self.mTimedRefreshPipeLine[QgsRasterRenderer])
if QgsFeatureRenderer in keys:
self.mapLayerModel().setDefaultVectorRenderer(self.mTimedRefreshPipeLine[QgsFeatureRenderer])
if QgsCoordinateReferenceSystem in keys:
self.setDestinationCrs(self.mTimedRefreshPipeLine[QgsCoordinateReferenceSystem])
if SpatialExtent in keys:
self.setSpatialExtent(self.mTimedRefreshPipeLine[SpatialExtent])
if SpatialPoint in keys:
self.setSpatialExtent(self.mTimedRefreshPipeLine[SpatialPoint])
if MapCanvas.Command in keys:
commands = self.mTimedRefreshPipeLine[MapCanvas.Command]
for command in commands:
assert isinstance(command, MapCanvas.Command)
if command == MapCanvas.Command.RefreshRenderer:
r = self.defaultRasterRenderer()
if isinstance(r, QgsRasterRenderer):
self.mapLayerModel().setDefaultRasterRenderer(r)
elif command == MapCanvas.Command.RefreshVisibility:
self.setLayers(self.visibleLayers())
elif command == MapCanvas.Command.RemoveRasters:
self.mMapLayerModel.removeMapLayerSources(self.mMapLayerModel.rasterSources())
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.RemoveVectors:
self.mMapLayerModel.removeMapLayerSources(self.mMapLayerModel.vectorSources())
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.ShowRasters:
self.mMapLayerModel.setLayerVisibility(QgsRasterLayer, True)
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.ShowVectors:
self.mMapLayerModel.setLayerVisibility(QgsVectorLayer, True)
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.HideRasters:
self.mMapLayerModel.setLayerVisibility(QgsRasterLayer, False)
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.HideVectors:
self.mMapLayerModel.setLayerVisibility(QgsVectorLayer, False)
self.setLayers(self.mMapLayerModel.visibleLayers())
elif command == MapCanvas.Command.Clear:
self.mMapLayerModel.clear()
s = ""
self.mTimedRefreshPipeLine.clear()
lyrs = self.layers()
visibleLayers = self.mapLayerModel().visibleLayers()
if lyrs != visibleLayers:
self.setLayers(visibleLayers)
self.freeze(False)
self.refresh()
#is this really required?
#if self.mNeedsRefresh or visibleLayers != lastLayers:
# self.mIsRefreshing = True
# self.mRefreshStartTime = time.time()
# self.setLayers(visibleLayers)
# self.refresh()
# self.mNeedsRefresh = False
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 timeseriesviewer.crosshair 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:
self.sigCrosshairStyleChanged.emit(self.mCrosshairItem.crosshairStyle)
def crosshairStyle(self)->CrosshairStyle:
"""
Returns the style of the Crosshair.
:return: CrosshairStyle
"""

Benjamin Jakimow
committed
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
return self.mCrosshairItem.crosshairStyle
def setCrosshairPosition(self, spatialPoint:SpatialPoint, emitSignal=True):
"""
Sets the position of the Crosshair.
:param spatialPoint: SpatialPoint
:param emitSignal: True (default). Set False to avoid emitting sigCrosshairPositionChanged
:return:
"""
point = spatialPoint.toCrs(self.mapSettings().destinationCrs())
self.mCrosshairItem.setPosition(point)
if emitSignal:
self.sigCrosshairPositionChanged.emit(point)
def crosshairPosition(self)->SpatialPoint:
"""Returns the last crosshair position"""
return self.mCrosshairItem.mPosition
def setCrosshairVisibility(self, b:bool, emitSignal=True):
"""
Sets the Crosshair visbility
: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()
def contextMenu(self)->QMenu:
"""
Create the MapCanvas context menu
:return:
"""
menu = QMenu()
# add general options
menu.addSeparator()
m = menu.addMenu('Stretch to current extent...')
action = m.addAction('Linear')
action.triggered.connect(lambda : self.stretchToExtent(self.spatialExtent(), 'linear_minmax', p=0.0))
action = m.addAction('Linear 5%')
action.triggered.connect(lambda: self.stretchToExtent(self.spatialExtent(), 'linear_minmax', p=0.05))
action = m.addAction('Gaussian')
action.triggered.connect(lambda: self.stretchToExtent(self.spatialExtent(), 'gaussian', n=3))
action = menu.addAction('Zoom to Layer')
action.triggered.connect(lambda : self.setSpatialExtent(self.spatialExtentHint()))
menu.addSeparator()
m = menu.addMenu('Crosshair...')
action = m.addAction('Show')
action.setCheckable(True)
action.setChecked(self.mCrosshairItem.visibility())
action.toggled.connect(self.setCrosshairVisibility)

Benjamin Jakimow
committed
def onCrosshairChange(*args):
style = CrosshairDialog.getCrosshairStyle(parent=self,
mapCanvas=self,
crosshairStyle=self.mCrosshairItem.crosshairStyle)

Benjamin Jakimow
committed
if isinstance(style, CrosshairStyle):
self.setCrosshairStyle(style)
action.triggered.connect(onCrosshairChange)
menu.addSeparator()
m = menu.addMenu('Copy...')
action = m.addAction('Date')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_date'))
action = m.addAction('Sensor')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_sensor'))
action = m.addAction('Path')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_path'))

benjamin.jakimow@geo.hu-berlin.de
committed
action = m.addAction('Map')
action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
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)')
action.triggered.connect(lambda: QApplication.clipboard().setText(center.asWkt()))

benjamin.jakimow@geo.hu-berlin.de
committed

benjamin.jakimow@geo.hu-berlin.de
committed
action.triggered.connect(lambda: QApplication.clipboard().setText(center.toString()))
action = m.addAction('Map Extent (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
action = m.addAction('Map Extent')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.toString()))

benjamin.jakimow@geo.hu-berlin.de
committed
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()))
action = m.addAction('PNG')
action.triggered.connect(lambda : self.saveMapImageDialog('PNG'))
action = m.addAction('JPEG')
action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))

benjamin.jakimow@geo.hu-berlin.de
committed

Benjamin Jakimow
committed
if isinstance(self.mLabelingModel, LabelAttributeTableModel) and isinstance(self.mTSD, TimeSeriesDatum):
menu.addSeparator()
m = self.mLabelingModel.contextMenuTSD(self.mTSD, menu)
from timeseriesviewer.utils import qgisInstance
actionAddRaster2QGIS = menu.addAction('Add raster layers(s) to QGIS')
actionAddRaster2QGIS.triggered.connect(lambda : self.addLayers2QGIS(
[l for l in self.layers() if isinstance(l, QgsRasterLayer)]
)
)
# QGIS 3: action.triggered.connect(lambda: QgsProject.instance().addMapLayers([l for l in self.layers() if isinstance(l, QgsRasterLayer)]))
actionAddVector2QGIS = menu.addAction('Add vector layer(s) to QGIS')
actionAddRaster2QGIS.triggered.connect(lambda : self.addLayers2QGIS(
#QgsProject.instance().addMapLayers(
[l for l in self.layers() if isinstance(l, QgsVectorLayer)]
)
)
# QGIS 3: action.triggered.connect(lambda: QgsProject.instance().addMapLayers([l for l in self.layers() if isinstance(l, QgsVectorLayer)]))
b = isinstance(qgisInstance(), QgisInterface)
for a in [actionAddRaster2QGIS, actionAddVector2QGIS]:
a.setEnabled(b)
menu.addSeparator()
action = menu.addAction('Hide date')
action.triggered.connect(lambda : self.sigChangeDVRequest.emit(self, 'hide_date'))
action = menu.addAction('Remove date')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'remove_date'))
action = menu.addAction('Hide map view')
action.triggered.connect(lambda: self.sigChangeMVRequest.emit(self, 'hide_mapview'))
action = menu.addAction('Remove map view')
action.triggered.connect(lambda: self.sigChangeMVRequest.emit(self, 'remove_mapview'))
return menu
def contextMenuEvent(self, event):
"""
Create and shows the MapCanvas context menu.
:param event: QEvent
"""
menu = self.contextMenu()
menu.exec_(event.globalPos())
def addLayers2QGIS(self, mapLayers):
from timeseriesviewer.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())
lqgis = iface.addVectorLayer(l.source(), l.name(), 'ogr')
def stretchToCurrentExtent(self):
se = self.spatialExtent()
def stretchToExtent(self, spatialExtent:SpatialExtent, stretchType='linear_minmax', **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:
"""
for l in self.layers():
if isinstance(l, QgsRasterLayer):
r = l.renderer()
dp = l.dataProvider()
newRenderer = None
assert isinstance(dp, QgsRasterDataProvider)
def getCE(band):
stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 500)
# hist = dp.histogram(band,100, stats.minimumValue, stats.maximumValue, extent, 500, False)
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)
return ce
if isinstance(r, QgsMultiBandColorRenderer):
#newRenderer = QgsMultiBandColorRenderer(None, r.redBand(), r.greenBand(), r.blueBand())
newRenderer = cloneRenderer(r)
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):
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 = cloneRenderer(r)
s = ""
elif isinstance(r, QgsPalettedRasterRenderer):
s = ""
if newRenderer is not None:
self.sigChangeSVRequest.emit(self, newRenderer)
s = ""
def saveMapImageDialog(self, fileType):
import timeseriesviewer.settings
lastDir = timeseriesviewer.settings.value(timeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.expanduser('~'))
from timeseriesviewer.utils import saveFilePath
from timeseriesviewer.mapvisualization import MapView
if isinstance(self.mTSD, TimeSeriesDatum) and isinstance(self.mMapView, MapView):
path = saveFilePath('{}.{}'.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)
timeseriesviewer.settings.setValue(timeseriesviewer.settings.Keys.ScreenShotDirectory, os.path.dirname(path))
"""
Returns the raster renderer in dependence of MapView and TimeSeriesDatum sensor
:return: QgsRasterRenderer
"""
from timeseriesviewer.mapvisualization import MapView
if isinstance(self.mTSD, TimeSeriesDatum) and isinstance(self.mMapView, MapView):
return self.mMapView.sensorWidget(self.mTSD.sensor()).rasterRenderer()
else:
return None
def setSpatialExtent(self, spatialExtent:SpatialExtent):
"""
Sets the SpatialExtent to be shown.
:param spatialExtent: SpatialExtent