mapvisualization.py 98.1 KB
Newer Older
1
# -*- coding: utf-8 -*-
Benjamin Jakimow's avatar
Benjamin Jakimow committed
2
# noinspection PyPep8Naming
3 4
"""
/***************************************************************************
5
                              EO Time Series Viewer
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
                              -------------------
        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.                                   *
 *                                                                         *
 ***************************************************************************/
"""
22

23 24 25 26 27 28
import os
import sys
import re
import fnmatch
import collections
import copy
29
import enum
30
import traceback
Benjamin Jakimow's avatar
Benjamin Jakimow committed
31 32 33
import typing

import numpy as np
34
import time
Benjamin Jakimow's avatar
Benjamin Jakimow committed
35
import qgis.utils
36
from qgis.PyQt.QtCore import \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
37
    Qt, QSize, pyqtSignal, QModelIndex, QTimer, QAbstractListModel, QMargins
38 39 40 41
from qgis.PyQt.QtGui import \
    QColor, QIcon, QGuiApplication, QMouseEvent
from qgis.PyQt.QtWidgets import \
    QWidget, QLayoutItem, QFrame, QLabel, QGridLayout, QSlider, QMenu, \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
42
    QToolBox, QDialog, QAction, QSpinBox, QCheckBox, QLineEdit, QWidgetItem, QSpacerItem, QLayout
43 44
from qgis.PyQt.QtXml import \
    QDomDocument, QDomNode, QDomElement
Benjamin Jakimow's avatar
Benjamin Jakimow committed
45

46
from qgis.core import \
47
    QgsCoordinateReferenceSystem, QgsVectorLayer, QgsTextFormat, QgsProject, \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
48
    QgsRectangle, QgsRasterRenderer, QgsMapLayerStore, QgsMapLayerStyle, \
49
    QgsLayerTreeModel, QgsLayerTreeGroup, QgsPointXY, \
50
    QgsLayerTree, QgsLayerTreeLayer, QgsReadWriteContext, QgsVector, \
51
    QgsRasterLayer, QgsVectorLayer, QgsMapLayer, QgsMapLayerProxyModel, QgsColorRamp, \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
52 53
    QgsSingleBandPseudoColorRenderer, QgsExpressionContextGenerator, QgsExpressionContext, \
    QgsExpressionContextUtils, QgsExpression, QgsExpressionContextScope
54 55
from qgis.gui import \
    QgsDockWidget, QgsMapCanvas, QgsMapTool, QgsCollapsibleGroupBox, QgsLayerTreeView, \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
56
    QgisInterface, QgsLayerTreeViewMenuProvider, QgsLayerTreeMapCanvasBridge, \
Benjamin Jakimow's avatar
Benjamin Jakimow committed
57 58
    QgsProjectionSelectionWidget, QgsMessageBar, QgsFieldExpressionWidget, QgsFilterLineEdit, \
    QgsExpressionLineEdit, QgsExpressionBuilderDialog, QgsAttributeForm
59
from .externals.qps.crosshair.crosshair import getCrosshairStyle, CrosshairStyle, CrosshairMapCanvasItem
Benjamin Jakimow's avatar
Benjamin Jakimow committed
60 61 62 63 64 65
from .externals.qps.layerproperties import VectorLayerTools
from .externals.qps.maptools import MapTools
from .mapcanvas import MapCanvas, MapCanvasInfoItem, KEY_LAST_CLICKED
from .timeseries import SensorInstrument, TimeSeriesDate, TimeSeries, SensorProxyLayer
from .utils import loadUi, SpatialPoint, SpatialExtent, datetime64
from eotimeseriesviewer import DIR_UI
66
from eotimeseriesviewer.utils import fixMenuButtons
67

68 69 70
KEY_LOCKED_LAYER = 'eotsv/locked'
KEY_SENSOR_GROUP = 'eotsv/sensorgroup'
KEY_SENSOR_LAYER = 'eotsv/sensorlayer'
71 72


Benjamin Jakimow's avatar
Benjamin Jakimow committed
73
def equalTextFormats(tf1: QgsTextFormat, tf2: QgsTextFormat) -> True:
74 75
    if not (isinstance(tf1, QgsTextFormat) and isinstance(tf2, QgsTextFormat)):
        return False
76
    return tf1.toMimeData().text() == tf2.toMimeData().text()
77

Benjamin Jakimow's avatar
Benjamin Jakimow committed
78

79 80 81 82
class MapViewLayerTreeModel(QgsLayerTreeModel):
    """
    Layer Tree as shown in a MapView
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
83

84 85 86
    def __init__(self, rootNode, parent=None):
        super(MapViewLayerTreeModel, self).__init__(rootNode, parent=parent)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
87

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
class MapViewExpressionContextGenerator(QgsExpressionContextGenerator):

    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)
        self.mMapView: MapView = None

    def setMapView(self, mapView):
        self.mMapView = mapView

    def createExpressionContext(self) -> QgsExpressionContext:
        context = QgsExpressionContext([QgsExpressionContextUtils.projectScope(QgsProject.instance())])

        if False and isinstance(self.mMapView, MapView):
            canvas = self.mMapView.currentMapCanvas()
            context.appendScope(canvas.expressionContextScope())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
103
        # self._context = context
104
        return context
Benjamin Jakimow's avatar
Benjamin Jakimow committed
105

Benjamin Jakimow's avatar
Benjamin Jakimow committed
106

107
class MapView(QFrame):
108 109 110
    """
    A MapView defines how a single map canvas visualizes sensor specific EOTS data plus additional vector overlays
    """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
111
    # sigVisibilityChanged = pyqtSignal(bool)
112 113
    sigCanvasAppearanceChanged = pyqtSignal()
    sigCrosshairChanged = pyqtSignal()
114
    sigTitleChanged = pyqtSignal(str)
115
    sigSensorRendererChanged = pyqtSignal(SensorInstrument, QgsRasterRenderer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
116
    sigCurrentLayerChanged = pyqtSignal(QgsMapLayer)
117
    sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
118

119 120
    def __init__(self, name='Map View', parent=None):
        super(MapView, self).__init__(parent)
121
        loadUi(DIR_UI / 'mapview.ui', self)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
122
        # self.setupUi(self)
123

124 125 126 127 128 129 130
        from eotimeseriesviewer.settings import values, Keys, defaultValues, value

        DEFAULT_VALUES = defaultValues()
        self.mMapBackgroundColor: QColor = value(Keys.MapBackgroundColor,
                                                 default=DEFAULT_VALUES.get(Keys.MapBackgroundColor, QColor('black')))
        self.mMapTextFormat: QgsTextFormat = value(Keys.MapTextFormat,
                                                   default=DEFAULT_VALUES.get(Keys.MapTextFormat, QgsTextFormat()))
131
        self.mMapWidget = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed
132

Benjamin Jakimow's avatar
Benjamin Jakimow committed
133
        self.mLayerStyleInitialized: typing.Dict[SensorInstrument, bool] = dict()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
134

135 136 137 138
        self.mTimeSeries = None
        self.mSensorLayerList = list()
        self.mCrossHairStyle = CrosshairStyle()

139 140
        m = QMenu(self.btnToggleCrosshair)
        m.addAction(self.actionSetCrosshairStyle)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
141

142 143
        self.btnToggleCrosshair.setMenu(m)
        self.btnToggleCrosshair.setDefaultAction(self.actionToggleCrosshairVisibility)
144
        self.btnToggleCrosshair.setChecked(self.crosshairStyle().mShow)
145
        self.btnToggleMapViewVisibility.setDefaultAction(self.actionToggleMapViewHidden)
146

147
        self.tbName.textChanged.connect(self.onTitleChanged)
148

149
        self.actionSetCrosshairStyle.triggered.connect(self.onChangeCrosshairStyle)
150
        self.actionToggleMapViewHidden.toggled.connect(self.sigCanvasAppearanceChanged)
151 152
        self.actionToggleCrosshairVisibility.toggled.connect(self.setCrosshairVisibility)

153 154 155 156 157 158 159 160
        self.actionAddMapLayer.triggered.connect(lambda *args: self.onAddMapLayer())
        self.actionAddVectorLayer.triggered.connect(lambda *args: self.onAddMapLayer(QgsMapLayerProxyModel.VectorLayer))
        self.actionAddRasterLayer.triggered.connect(lambda *args: self.onAddMapLayer(QgsMapLayerProxyModel.RasterLayer))
        self.btnAddLayer.setDefaultAction(self.actionAddMapLayer)
        m = QMenu()
        m.addAction(self.actionAddVectorLayer)
        m.addAction(self.actionAddRasterLayer)
        self.btnAddLayer.setMenu(m)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
161

162 163
        self.btnHighlightMapView.setDefaultAction(self.actionHighlightMapView)
        self.actionHighlightMapView.triggered.connect(lambda: self.setHighlighted(True, timeout=500))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
164

165
        assert isinstance(self.mLayerTreeView, QgsLayerTreeView)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
166

Benjamin Jakimow's avatar
Benjamin Jakimow committed
167
        self.mDummyCanvas = QgsMapCanvas()  # dummy map canvas for dummy layers
168
        self.mDummyCanvas.setVisible(False)
169

170
        self.mLayerTree = QgsLayerTree()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
171
        self.mLayerTreeMapCanvasBridge = QgsLayerTreeMapCanvasBridge(self.mLayerTree, self.mDummyCanvas)
172

173 174
        # self.mLayerTreeModel = QgsLayerTreeModel(self.mLayerTree)
        self.mLayerTreeModel = MapViewLayerTreeModel(self.mLayerTree)
175

176 177 178
        self.mLayerTreeModel.setFlags(QgsLayerTreeModel.AllowNodeChangeVisibility |
                                      QgsLayerTreeModel.AllowNodeRename |
                                      QgsLayerTreeModel.AllowNodeReorder)
179

180
        self._createSensorNode()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
181
        self.mLayerTreeView: QgsLayerTreeView
182
        self.mLayerTreeView.setModel(self.mLayerTreeModel)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
183
        self.mLayerTreeView.currentLayerChanged.connect(self.sigCurrentLayerChanged.emit)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
184 185
        self.mMapLayerTreeViewMenuProvider = MapViewLayerTreeViewMenuProvider(self, self.mLayerTreeView,
                                                                              self.mDummyCanvas)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
186 187

        # register some actions that interact with other GUI elements
Benjamin Jakimow's avatar
Benjamin Jakimow committed
188 189
        # self.mMapLayerTreeViewMenuProvider.actionAddEOTSVSpectralProfiles.triggered.connect(self.addSpectralProfileLayer)
        # self.mMapLayerTreeViewMenuProvider.actionAddEOTSVTemporalProfiles.triggered.connect(self.addTemporalProfileLayer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
190

191
        self.mLayerTreeView.setMenuProvider(self.mMapLayerTreeViewMenuProvider)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
192
        self.mLayerTreeView.currentLayerChanged.connect(self.setCurrentLayer)
193
        self.mLayerTree.removedChildren.connect(self.onChildNodesRemoved)
194

195
        self.mIsVisible = True
Benjamin Jakimow's avatar
Benjamin Jakimow committed
196
        self.setTitle(name)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
197
        self.tbInfoExpression: QLineEdit
198 199
        self.mDefaultInfoExpressionToolTip:str = self.tbInfoExpression.toolTip()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
200 201 202 203 204 205 206 207
        self.tbInfoExpression.textChanged.connect(self.onMapInfoExpressionChanged)
        self.btnShowInfoExpression.setDefaultAction(self.optionShowInfoExpression)
        self.optionShowInfoExpression.toggled.connect(self.tbInfoExpression.setEnabled)
        self.optionShowInfoExpression.toggled.connect(self.actionSetInfoExpression.setEnabled)
        self.btnSetInfoExpression.setDefaultAction(self.actionSetInfoExpression)
        self.actionSetInfoExpression.triggered.connect(self.onSetInfoExpression)

        self._fakeLyr: QgsVectorLayer = QgsVectorLayer("point?crs=epsg:4326", "Scratch point layer", "memory")
208

Benjamin Jakimow's avatar
Benjamin Jakimow committed
209
        # self.tbInfoExpression.setLayer(self.mLyr)
210

Benjamin Jakimow's avatar
Benjamin Jakimow committed
211 212
        # self.mExpressionContextGenerator = MapViewExpressionContextGenerator()
        # self.mExpressionContextGenerator.setMapView(self)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
213 214

        self.tbInfoExpression.setEnabled(self.optionShowInfoExpression.isChecked())
215

Benjamin Jakimow's avatar
Benjamin Jakimow committed
216
        # self.tbInfoExpression.registerExpressionContextGenerator(self.mExpressionContextGenerator)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
217
        self.optionShowInfoExpression.toggled.connect(self.sigCanvasAppearanceChanged)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
218
        # self.tbInfoExpression.expressionChanged.connect(self.sigCanvasAppearanceChanged)
219 220 221
        for action in m.actions():
            action.toggled.connect(self.sigCanvasAppearanceChanged)

222 223
        fixMenuButtons(self)

224 225 226 227 228 229 230 231 232 233
    def setInfoExpressionError(self, error: str):

        if error in ['', None]:
            self.tbInfoExpression.setStyleSheet('')
            self.tbInfoExpression.setToolTip(self.mDefaultInfoExpressionToolTip)
        else:

            self.tbInfoExpression.setStyleSheet('QLineEdit#tbInfoExpression{color:red; border: 2px solid red;}')
            self.tbInfoExpression.setToolTip(f'<span style="color:red">{error}</span>')

Benjamin Jakimow's avatar
Benjamin Jakimow committed
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250
    def onMapInfoExpressionChanged(self, text: str):

        self.sigCanvasAppearanceChanged.emit()
        s = ""

    def onSetInfoExpression(self, *args):

        context = QgsExpressionContext(QgsExpressionContextUtils.globalProjectLayerScopes(self._fakeLyr))
        c = self.currentMapCanvas()
        if isinstance(c, MapCanvas):
            context.appendScope(QgsExpressionContextScope(c.expressionContextScope()))
        expression = self.tbInfoExpression.text()
        # taken from qgsfeaturefilterwidget.cpp : void QgsFeatureFilterWidget::filterExpressionBuilder()
        dlg = QgsExpressionBuilderDialog(self._fakeLyr, expression,
                                         self,
                                         'generic', context)
        dlg.setWindowTitle('Expression Based Filter')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
251 252 253 254
        # myDa = QgsDistanceArea()
        # myDa.setSourceCrs(self.mLayer.crs(), QgsProject.instance().transformContext())
        # myDa.setEllipsoid(QgsProject.instance().ellipsoid())
        # dlg.setGeomCalculator(myDa)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
255 256 257

        if dlg.exec() == QDialog.Accepted:
            self.tbInfoExpression.setText(dlg.expressionText())
258

259 260 261
    def __iter__(self) -> typing.Iterator[TimeSeriesDate]:
        return iter(self.mapCanvases())

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
    @staticmethod
    def readXml(node: QDomNode):
        if node.nodeName() == 'MapView':
            nodeMapView = node
        else:
            nodeMapView = node.firstChildElement('MapView')

        if nodeMapView.nodeName() != 'MapView':
            return None

        context = QgsReadWriteContext()
        mapView = MapView()

        def to_bool(value) -> bool:
            return str(value).lower() in ['1', 'true']

        mapView.setName(nodeMapView.attribute('name'))
        mapView.setMapBackgroundColor(QColor(nodeMapView.attribute('bg')))
        mapView.setVisibility(to_bool(nodeMapView.attribute('visible')))
281

Benjamin Jakimow's avatar
Benjamin Jakimow committed
282 283 284
        # mapView.optionShowDate.setChecked(to_bool(nodeMapView.attribute('showDate')))
        # mapView.optionShowSensorName.setChecked(to_bool(nodeMapView.attribute('showSensorName')))
        # mapView.optionShowMapViewName.setChecked(to_bool(nodeMapView.attribute('showMapViewName')))
285

286 287 288
        # nodeMapView.setAttribute('showDate', str(self.optionShowDate.checked()))
        # nodeMapView.setAttribute('showSensorName', str(self.optionShowSensorName.checked()))
        # nodeMapView.setAttribute('showMapViewName', str(self.optionShowMapViewName.checked()))
289 290 291 292 293 294 295 296 297 298 299 300 301 302

        textFormat = mapView.mapTextFormat()
        textFormat.readXml(nodeMapView, context)
        lyrNode = node.firstChildElement('MapViewProxyLayer').toElement()
        while lyrNode.nodeName() == 'MapViewProxyLayer':
            sid = lyrNode.attribute('sensor_id')
            styleNode = lyrNode.firstChildElement('LayerStyle')
            style = QgsMapLayerStyle()
            style.readXml(styleNode)
            sensor = SensorInstrument(sid)
            mapView.addSensor(sensor)
            lyr = mapView.sensorProxyLayer(sensor)
            lyr.setMapLayerStyle(style)

303
            lyrNode = lyrNode.nextSiblingElement()
304 305 306 307 308 309 310 311
        return mapView

    def writeXml(self, node: QDomNode, doc: QDomDocument):

        nodeMapView = doc.createElement('MapView')
        nodeMapView.setAttribute('name', self.name())
        nodeMapView.setAttribute('bg', self.mapBackgroundColor().name())
        nodeMapView.setAttribute('visible', str(self.isVisible()))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
312
        nodeMapView.setAttribute('infoexpression', self.mapInfoExpression())
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329

        context = QgsReadWriteContext()
        nodeTextStyle = self.mapTextFormat().writeXml(doc, context)
        nodeMapView.appendChild(nodeTextStyle)

        for sensor in self.sensors():
            lyr = self.sensorProxyLayer(sensor)
            if isinstance(lyr, SensorProxyLayer):
                sensorNode = doc.createElement('MapViewProxyLayer')
                sensorNode.setAttribute('sensor_id', sensor.id())
                style: QgsMapLayerStyle = lyr.mapLayerStyle()
                styleNode = doc.createElement('LayerStyle')
                style.writeXml(styleNode)
                sensorNode.appendChild(styleNode)
                nodeMapView.appendChild(sensorNode)
        node.appendChild(nodeMapView)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
330
    def setName(self, name: str):
331 332
        self.setTitle(name)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
333
    def name(self) -> str:
334 335
        return self.title()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
336 337 338 339 340 341 342
    def setMapInfoExpression(self, expression: str):
        self.tbInfoExpression.setText(expression)

    def mapInfoExpression(self) -> str:

        return f'{self.tbInfoExpression.text()}'.strip()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
343
    def setMapTextFormat(self, textformat: QgsTextFormat) -> QgsTextFormat:
344 345 346 347 348
        if not equalTextFormats(self.mapTextFormat(), textformat):
            self.mMapTextFormat = textformat
            self.sigCanvasAppearanceChanged.emit()
        return self.mapTextFormat()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
349
    def mapTextFormat(self) -> QgsTextFormat:
350 351
        return self.mMapTextFormat

Benjamin Jakimow's avatar
Benjamin Jakimow committed
352
    def mapBackgroundColor(self) -> QColor:
353 354 355 356 357 358
        """
        Returns the map background color
        :return: QColor
        """
        return self.mMapBackgroundColor

Benjamin Jakimow's avatar
Benjamin Jakimow committed
359
    def setMapBackgroundColor(self, color: QColor) -> QColor:
360 361 362 363 364 365 366 367 368 369
        """
        Sets the map background color
        :param color: QColor
        :return: QColor
        """
        if self.mMapBackgroundColor != color:
            self.mMapBackgroundColor = color
            self.sigCanvasAppearanceChanged.emit()
        return self.mMapBackgroundColor

Benjamin Jakimow's avatar
Benjamin Jakimow committed
370
    def visibleMapCanvases(self) -> list:
371 372 373 374 375 376
        """
        Returns the currently visible mapcanvases
        :return: [list-of-MapCanvases]
        """
        return [m for m in self.mapCanvases() if m.isVisibleToViewport()]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
377
    def onAddMapLayer(self, filter: QgsMapLayerProxyModel.Filter = QgsMapLayerProxyModel.All):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
378
        """
379
        Slot that opens a SelectMapLayersDialog for any kind of layer
Benjamin Jakimow's avatar
Benjamin Jakimow committed
380 381 382
        """
        from .externals.qps.utils import SelectMapLayersDialog
        d = SelectMapLayersDialog()
383 384 385 386 387 388 389 390 391 392 393 394

        if filter == QgsMapLayerProxyModel.All:
            title = 'Select Layer'
            text = 'Layer'
        elif filter == QgsMapLayerProxyModel.RasterLayer:
            title = 'Select Raster Layer'
            text = 'Raster'
        elif filter == QgsMapLayerProxyModel.VectorLayer:
            title = 'Select Vector Layer'
            text = 'Vector'
        d.setWindowTitle(title)
        d.addLayerDescription(text, filter)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
395 396 397
        if d.exec() == QDialog.Accepted:
            for l in d.mapLayers():
                self.addLayer(l)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
398

Benjamin Jakimow's avatar
Benjamin Jakimow committed
399
    def setCurrentLayer(self, layer: QgsMapLayer):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
400 401 402 403 404
        """
        Sets the QgsMapCanvas.currentLayer() that is used by some QgsMapTools
        :param layer: QgsMapLayer | None
        :return:
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
405
        assert layer is None or isinstance(layer, QgsMapLayer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
406 407
        if layer in self.layers():
            self.mLayerTreeView.setCurrentLayer(layer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
408

Benjamin Jakimow's avatar
Benjamin Jakimow committed
409 410 411
            if layer not in self.mSensorLayerList:
                for c in self.mapCanvases():
                    c.setCurrentLayer(layer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
412
            return True
Benjamin Jakimow's avatar
Benjamin Jakimow committed
413

414 415
    def addSpectralProfileLayers(self):
        """Adds the EOTSV Spectral Profile Layers"""
Benjamin Jakimow's avatar
main.py  
Benjamin Jakimow committed
416 417 418
        from eotimeseriesviewer.main import EOTimeSeriesViewer
        tsv = EOTimeSeriesViewer.instance()
        if isinstance(tsv, EOTimeSeriesViewer):
419 420 421
            for lyr in tsv.spectralLibraries():
                if lyr not in self.layers():
                    self.addLayer(lyr)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
422 423 424

    def addTemporalProfileLayer(self):
        """Adds the EOTSV Temporal Profile Layer"""
Benjamin Jakimow's avatar
main.py  
Benjamin Jakimow committed
425 426 427
        from eotimeseriesviewer.main import EOTimeSeriesViewer
        tsv = EOTimeSeriesViewer.instance()
        if isinstance(tsv, EOTimeSeriesViewer):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
428 429 430 431
            lyr = tsv.temporalProfileLayer()
            if lyr not in self.layers():
                self.addLayer(lyr)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
432
    def addLayer(self, layer: QgsMapLayer):
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
        """
        Add a QgsMapLayer to the MapView layer tree
        :param layer: QgsMapLayer
        """
        if isinstance(layer, QgsVectorLayer):
            self.mLayerTree.insertLayer(0, layer)
        else:
            self.mLayerTree.addLayer(layer)

    def _createSensorNode(self):
        self.mLayerTreeSensorNode = QgsLayerTreeGroup(name='Raster Time Series', checked=True)
        self.mLayerTreeSensorNode.setCustomProperty(KEY_LOCKED_LAYER, True)
        self.mLayerTreeSensorNode.setCustomProperty(KEY_SENSOR_GROUP, True)
        self.mLayerTree.addChildNode(self.mLayerTreeSensorNode)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
448
    def _containsSensorNode(self, root: QgsLayerTreeGroup) -> bool:
449 450 451 452 453 454 455 456 457 458 459 460 461
        assert isinstance(root, QgsLayerTreeGroup)
        if root.customProperty(KEY_SENSOR_GROUP) in [True, 'true']:
            return True
        for grp in root.findGroups():
            if self._containsSensorNode(grp):
                return True
        return False

    def onChildNodesRemoved(self, node, idxFrom, idxTo):
        if not self._containsSensorNode(self.mLayerTreeModel.rootGroup()):
            self._createSensorNode()

    def onChangeCrosshairStyle(self):
462 463 464 465 466 467 468 469

        canvases = self.mapCanvases()
        if len(canvases) > 0:
            mapCanvas = canvases[0]
        else:
            mapCanvas = None

        style = getCrosshairStyle(parent=self, crosshairStyle=self.crosshairStyle(), mapCanvas=mapCanvas)
470 471 472
        if isinstance(style, CrosshairStyle):
            self.setCrosshairStyle(style)

473
    def setVisibility(self, b: bool):
474 475 476 477
        """
        Sets the map view visibility
        :param b: bool
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
478
        assert isinstance(b, bool)
benjamin.jakimow@geo.hu-berlin.de's avatar
tbd  
benjamin.jakimow@geo.hu-berlin.de committed
479

480
        changed = False
Benjamin Jakimow's avatar
Benjamin Jakimow committed
481

482 483
        if self.actionToggleMapViewHidden.isChecked() == b:
            self.actionToggleMapViewHidden.setChecked(not b)
benjamin.jakimow@geo.hu-berlin.de's avatar
tbd  
benjamin.jakimow@geo.hu-berlin.de committed
484

benjamin.jakimow@geo.hu-berlin.de's avatar
tbd  
benjamin.jakimow@geo.hu-berlin.de committed
485
        if changed:
486
            self.sigCanvasAppearanceChanged.emit()
benjamin.jakimow@geo.hu-berlin.de's avatar
tbd  
benjamin.jakimow@geo.hu-berlin.de committed
487

Benjamin Jakimow's avatar
Benjamin Jakimow committed
488
    def isVisible(self) -> bool:
489 490 491 492
        """
        Returns the map view visibility
        :return: bool
        """
493
        return not self.actionToggleMapViewHidden.isChecked()
494

Benjamin Jakimow's avatar
Benjamin Jakimow committed
495 496 497
    def mapWidget(self):
        return self.mMapWidget

Benjamin Jakimow's avatar
Benjamin Jakimow committed
498
    def mapCanvases(self) -> typing.List[MapCanvas]:
499
        """
500
        Returns the MapCanvases related to this map view. Requires that this mapview was added to a MapWidget
501 502
        :return: [list-of-MapCanvases]
        """
503 504 505 506 507

        if isinstance(self.mMapWidget, MapWidget):
            return self.mMapWidget.mapViewCanvases(self)
        else:
            return []
508

509
    def onTitleChanged(self, *args):
510

511
        self.setWindowTitle('Map View "{}"'.format(self.title()))
512
        self.sigTitleChanged.emit(self.title())
513
        self.sigCanvasAppearanceChanged.emit()
514 515 516 517 518 519

    def setMapWidget(self, w):
        if isinstance(w, MapWidget):
            self.mMapWidget = w
        else:
            self.mMapWidget = None
520

Benjamin Jakimow's avatar
Benjamin Jakimow committed
521
    def setTimeSeries(self, timeSeries: TimeSeries):
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
        """
        Conntects the MapView with a TimeSeries.
        :param timeSeries: TimeSeries
        """
        assert isinstance(timeSeries, TimeSeries)

        for s in self.sensors():
            self.removeSensor(s)

        self.mTimeSeries = timeSeries
        self.mTimeSeries.sigSensorAdded.connect(self.addSensor)
        self.mTimeSeries.sigSensorRemoved.connect(self.removeSensor)
        for s in timeSeries.sensors():
            self.addSensor(s)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
537
    def timeSeries(self) -> TimeSeries:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
538 539 540 541 542
        """
        Returns the TimeSeries this mapview is connected with
        :return: TimeSeries
        """
        return self.mTimeSeries
543

Benjamin Jakimow's avatar
Benjamin Jakimow committed
544
    def setTitle(self, title: str):
545 546 547 548
        """
        Sets the widget title
        :param title: str
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
549 550
        old = self.title()
        if old != title:
551
            self.tbName.setText(title)
552

Benjamin Jakimow's avatar
Benjamin Jakimow committed
553
    def visibleLayers(self) -> typing.List[QgsMapLayer]:
554 555 556 557
        """
        Returns the visible layers, including proxy layer for time-series data
        :return: [list-of-QgsMapLayers]
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
558
        return [l for l in self.mLayerTree.checkedLayers() if isinstance(l, QgsMapLayer)]
559

Benjamin Jakimow's avatar
Benjamin Jakimow committed
560 561 562 563 564 565 566 567
    def layers(self) -> typing.List[QgsMapLayer]:
        """
        Returns all layers, including invisible or proxy layers for time-series data
        :return: [list-of-QgsMapLayers]
        """
        nodes = self.mLayerTree.findLayers()
        return [n.layer() for n in nodes if isinstance(n.layer(), QgsMapLayer)]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
568
    def title(self, maskNewLines=True) -> str:
569 570 571 572
        """
        Returns the MapView title
        :return: str
        """
573 574 575 576
        if maskNewLines:
            return self.tbName.text().replace('\\n', ' ').strip()
        else:
            return self.tbName.text().strip()
577

Benjamin Jakimow's avatar
Benjamin Jakimow committed
578
    def setCrosshairStyle(self, crosshairStyle: CrosshairStyle) -> CrosshairStyle:
579
        """
580
        Seths the CrosshairStyle of this MapView
581 582
        :param crosshairStyle: CrosshairStyle
        """
583

584 585 586 587 588
        if self.mCrossHairStyle != crosshairStyle:
            self.mCrossHairStyle = crosshairStyle
            self.sigCrosshairChanged.emit()

        return self.mCrossHairStyle
589

590
    def setHighlighted(self, b=True, timeout=1000):
591 592 593 594 595
        """
        Activates or deactivates a red-line border of the MapCanvases
        :param b: True | False to activate / deactivate the highlighted lines-
        :param timeout: int, milliseconds how long the highlighted frame should appear
        """
596 597 598 599 600 601 602 603 604
        styleOn = """.MapCanvas {
                    border: 4px solid red;
                    border-radius: 4px;
                }"""
        styleOff = """"""
        if b is True:
            for mapCanvas in self.mapCanvases():
                mapCanvas.setStyleSheet(styleOn)
            if timeout > 0:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
605
                QTimer.singleShot(timeout, lambda: self.setHighlighted(False))
606 607 608 609
        else:
            for mapCanvas in self.mapCanvases():
                mapCanvas.setStyleSheet(styleOff)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
610
    def currentMapCanvas(self) -> MapCanvas:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
611 612 613 614
        """
        Returns the MapCanvas that was clicked / used last
        :return: MapCanvas
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
615 616 617
        if not isinstance(self.mMapWidget, MapWidget):
            return None
        canvases = sorted(self.mMapWidget.mapViewCanvases(self),
Benjamin Jakimow's avatar
Benjamin Jakimow committed
618
                          key=lambda c: c.property(KEY_LAST_CLICKED))
Benjamin Jakimow's avatar
Benjamin Jakimow committed
619 620 621 622 623
        if len(canvases) == 0:
            return None
        else:
            return canvases[-1]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
624 625
    def currentLayer(self) -> QgsMapLayer:
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
626 627 628 629 630
        Returns the current map layer, i.e. that selected in the map layer tree view.
        If this is a proxy layer, the MapView will try to return a "real" layer from a MapCanvas


        :return: QgsMapLayer
Benjamin Jakimow's avatar
Benjamin Jakimow committed
631
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
632 633 634
        cl = self.mLayerTreeView.currentLayer()
        if isinstance(cl, SensorProxyLayer):
            sensor = cl.sensor()
635 636 637
            canvases = [c for c in self.mapCanvases() if c.tsd().sensor() == sensor]
            canvases = sorted(canvases, key=lambda c: c is not self.currentMapCanvas())
            for c in canvases:
Benjamin Jakimow's avatar
Benjamin Jakimow committed
638 639 640 641
                for l in c.layers():
                    if isinstance(l, SensorProxyLayer):
                        return l
        return cl
Benjamin Jakimow's avatar
Benjamin Jakimow committed
642

Benjamin Jakimow's avatar
Benjamin Jakimow committed
643
    def crosshairStyle(self) -> CrosshairStyle:
644 645
        """
        Returns the CrosshairStyle
646
        :return: CrosshairStyle
647
        """
648
        return self.mCrossHairStyle
649

Benjamin Jakimow's avatar
Benjamin Jakimow committed
650
    def setCrosshairVisibility(self, b: bool):
651
        """
652 653
        Enables / diables the map canvas crosshair.
        :param b: bool
654
        """
655
        if b != self.actionToggleCrosshairVisibility.isChecked():
656
            self.actionToggleCrosshairVisibility.setChecked(b)
657
        else:
658 659 660
            self.mCrossHairStyle.setVisibility(b)
            self.sigCrosshairChanged.emit()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
661
    def sensorProxyLayers(self) -> typing.List[SensorProxyLayer]:
662 663
        layers = [n.layer() for n in self.mLayerTreeSensorNode.findLayers()]
        return [l for l in layers if isinstance(l, SensorProxyLayer)]
664

Benjamin Jakimow's avatar
Benjamin Jakimow committed
665
    def sensorProxyLayer(self, sensor: SensorInstrument) -> SensorProxyLayer:
666
        """
667 668 669 670 671 672 673 674 675
        Returns the proxy layer related to a SensorInstrument
        :param sensor: SensorInstrument
        :return: SensorLayer
        """
        for l in self.sensorProxyLayers():
            if l.sensor() == sensor:
                return l
        return None

Benjamin Jakimow's avatar
Benjamin Jakimow committed
676 677 678 679 680 681 682 683 684 685 686 687 688
    def sensorLayers(self, sensor: SensorInstrument) -> typing.List[SensorProxyLayer]:
        """
        :param sensor:
        :return:
        """
        layers = []
        for c in self.mapCanvases():
            for l in c.layers():
                if isinstance(l, SensorProxyLayer) and l.sensor() == sensor:
                    layers.append(l)
        return layers

    def sensors(self) -> typing.List[SensorInstrument]:
689 690 691 692 693 694
        """
        Returns a list of SensorsInstruments
        :return: [list-of-SensorInstruments]
        """
        return [t[0] for t in self.mSensorLayerList]

Benjamin Jakimow's avatar
Benjamin Jakimow committed
695
    def addSensor(self, sensor: SensorInstrument):
696 697 698 699 700 701 702
        """
        Adds a SensorInstrument to be shown in this MapView. Each sensor will be represented as a Raster Layer in the
        Tree Model.
        :param sensor: SensorInstrument
        """
        assert isinstance(sensor, SensorInstrument)
        if sensor not in self.sensors():
703
            sensor.sigNameChanged.connect(self.sigCanvasAppearanceChanged)
704

Benjamin Jakimow's avatar
Benjamin Jakimow committed
705 706
            masterLayer: SensorProxyLayer = sensor.proxyRasterLayer()
            assert isinstance(masterLayer.renderer(), QgsRasterRenderer)
707

Benjamin Jakimow's avatar
Benjamin Jakimow committed
708 709
            self.mSensorLayerList.append((sensor, masterLayer))
            masterLayer.styleChanged.connect(lambda *args, v=self, l=masterLayer: self.onMasterStyleChanged(l))
710
            masterLayer.nameChanged.connect(self.onMasterLyrNameChanged)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
711
            layerTreeLayer: QgsLayerTreeLayer = self.mLayerTreeSensorNode.addLayer(masterLayer)
712 713 714
            layerTreeLayer.setCustomProperty(KEY_LOCKED_LAYER, True)
            layerTreeLayer.setCustomProperty(KEY_SENSOR_LAYER, True)

715 716 717
            dummyLayers = self.mDummyCanvas.layers() + [masterLayer]
            self.mDummyCanvas.setLayers(dummyLayers)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
718 719
            self.mLayerStyleInitialized[sensor] = False

720 721 722 723
    def onMasterLyrNameChanged(self, *args):
        lyr = self.sender()
        newname = lyr.name()
        ltn = self.mLayerTreeSensorNode.findLayer(lyr)
724
        # print(ltn.name())
725

Benjamin Jakimow's avatar
Benjamin Jakimow committed
726 727 728 729
    def onMasterStyleChanged(self, masterLayer: SensorProxyLayer):

        sensor: SensorInstrument = masterLayer.sensor()
        style: QgsMapLayerStyle = masterLayer.mapLayerStyle()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
730 731
        # print('### MASTER-STYLE-CHANGED')
        # print(style.xmlData())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
732 733 734
        for lyr in self.sensorLayers(sensor):
            lyr.setMapLayerStyle(style)

735 736 737
        for c in self.sensorCanvases(sensor):
            assert isinstance(c, MapCanvas)
            c.addToRefreshPipeLine(MapCanvas.Command.RefreshRenderer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
738 739

        self.mLayerStyleInitialized[sensor] = True
740

Benjamin Jakimow's avatar
Benjamin Jakimow committed
741
    def sensorCanvases(self, sensor: SensorInstrument) -> list:
742 743 744 745 746 747
        """
        Returns the MapCanvases that show a layer with data for the given ``sensor``
        :param sensor: SensorInstrument
        :return:
        """
        assert isinstance(sensor, SensorInstrument)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
748 749
        return [c for c in self.mapCanvases() if isinstance(c, MapCanvas) and \
                isinstance(c.tsd(), TimeSeriesDate) and c.tsd().sensor() == sensor]
750

751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
    def sensorLayer(self, sensor: SensorInstrument):
        """
        Returns the QgsRasterLayer that is used a proxy to specify the QgsRasterRenderer for a sensor
        :param sensor: SensorInstrument
        :return: QgsRasterLayer
        """
        assert isinstance(sensor, SensorInstrument)
        for t in self.mSensorLayerList:
            s, l = t
            assert isinstance(s, SensorInstrument)
            assert isinstance(l, QgsRasterLayer)
            if s == sensor:
                return l
        raise Exception('Sensor "{}" not registered to MapView "{}"'.format(sensor.name(), self.title()))

Benjamin Jakimow's avatar
Benjamin Jakimow committed
766
    def removeSensor(self, sensor: SensorInstrument):
767 768
        """
        Removes a sensor from this map view
769 770 771
        :param sensor:
        :return:
        """
Benjamin Jakimow's avatar
Benjamin Jakimow committed
772 773
        if sensor in self.mLayerStyleInitialized.keys():
            self.mLayerStyleInitialized.pop(sensor)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
774

775 776
        toRemove = []
        for t in self.mSensorLayerList:
777
            if t[0] == sensor:
778 779 780 781 782
                toRemove.append(t)

        for t in toRemove:
            self.mLayerTreeSensorNode.removeLayer(t[1])
            self.mSensorLayerList.remove(t)
783

784
    def hasSensor(self, sensor: SensorInstrument) -> bool:
785 786 787 788 789 790
        """
        :param sensor:
        :return:
        """
        assert isinstance(sensor, SensorInstrument)
        return sensor in self.sensors()
791

Benjamin Jakimow's avatar
Benjamin Jakimow committed
792 793 794 795 796 797 798 799 800

class MapViewLayerTreeViewMenuProvider(QgsLayerTreeViewMenuProvider):

    def __init__(self, mapView, view: QgsLayerTreeView, canvas: QgsMapCanvas):
        super(MapViewLayerTreeViewMenuProvider, self).__init__()
        assert isinstance(view, QgsLayerTreeView)
        assert isinstance(canvas, QgsMapCanvas)
        self.mLayerTreeView: QgsLayerTreeView = view
        self.mDummyCanvas: QgsMapCanvas = canvas
Benjamin Jakimow's avatar
Benjamin Jakimow committed
801
        # self.mDefActions = QgsLayerTreeViewDefaultActions(self.mLayerTreeView)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
802
        self.mMapView: MapView = mapView
Benjamin Jakimow's avatar
Benjamin Jakimow committed
803 804 805 806 807 808 809 810 811 812 813
        # self.actionAddGroup = self.mDefActions.actionAddGroup()
        # self.actionRename = self.mDefActions.actionRenameGroupOrLayer()
        # self.actionRemove = self.mDefActions.actionRemoveGroupOrLayer()
        # self.actionZoomToLayer = self.mDefActions.actionZoomToGroup(self.mDummyCanvas)
        # self.actionCheckAndAllChildren = self.mDefActions.actionCheckAndAllChildren()
        # self.actionShowFeatureCount = self.mDefActions.actionShowFeatureCount()
        # self.actionZoomToLayer = self.mDefActions.actionZoomToLayer(self.mDummyCanvas)
        # self.actionZoomToSelected = self.mDefActions.actionZoomToSelection(self.mDummyCanvas)
        # self.actionZoomToGroup = self.mDefActions.actionZoomToGroup(self.mDummyCanvas)
        # self.actionAddEOTSVSpectralProfiles = QAction('Add Spectral Profile Layer')
        # self.actionAddEOTSVTemporalProfiles = QAction('Add Temporal Profile Layer')
Benjamin Jakimow's avatar
Benjamin Jakimow committed
814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836

    def mapView(self) -> MapView:
        return self.mMapView

    def layerTreeView(self) -> QgsLayerTreeView:
        return self.mLayerTreeView

    def layerTree(self) -> QgsLayerTree:
        return self.layerTreeModel().rootGroup()

    def layerTreeModel(self) -> QgsLayerTreeModel:
        return self.layerTreeView().model()

    def onRemoveLayers(self):
        selected = self.layerTreeView().selectedLayers()
        for l in selected:
            if not isinstance(l, SensorProxyLayer):
                self.mapView().mLayerTree.removeLayer(l)

    def onSetCanvasCRS(self):
        s = ""
        lyr = self.layerTreeView()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
837
    def onZoomToLayer(self, layer: QgsMapLayer):
Benjamin Jakimow's avatar
Benjamin Jakimow committed
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866
        extent = SpatialExtent.fromLayer(layer)
        if isinstance(extent, SpatialExtent):
            extent = extent.toCrs(self.mapView().mapWidget().crs())
            self.mapView().mapWidget().setSpatialExtent(extent)

    def onZoomActualSize(self):
        current = self.mapView().currentLayer()
        if isinstance(current, QgsRasterLayer):
            s = ""

    def onStretchToExtent(self):
        current = self.mapView().currentLayer()
        canvas = self.mapView().currentMapCanvas()
        if not isinstance(canvas, MapCanvas):
            return
        if isinstance(current, SensorProxyLayer):

            for l in canvas.layers():
                if isinstance(l, SensorProxyLayer) and l.sensor() == current.sensor():
                    canvas.stretchToExtent(layer=current)
                    break

        elif isinstance(current, QgsRasterLayer):
            canvas.stretchToExtent(layer=current)

    def createContextMenu(self) -> QMenu:

        model = self.layerTreeModel()
        ltree = self.layerTree()
867
        view: QgsLayerTreeView = self.layerTreeView()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
868 869
        currentGroup = view.currentGroupNode()
        currentLayer = view.currentLayer()
870
        currentIndex = view.currentIndex()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
871

Benjamin Jakimow's avatar
Benjamin Jakimow committed
872
        currentCanvas = self.mapView().currentMapCanvas()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
873 874
        isSensorGroup = isinstance(currentGroup, QgsLayerTreeGroup) and currentGroup.customProperty(
            KEY_SENSOR_GROUP) in [True, 'true']
Benjamin Jakimow's avatar
Benjamin Jakimow committed
875
        isSensorLayer = isinstance(currentLayer, SensorProxyLayer)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
876 877 878 879 880 881 882
        mv: MapView = self.mapView()
        mw: MapWidget = mv.mapWidget()
        mw.setCurrentMapView(mv)
        if isSensorLayer:
            # the current layer is an "empty" proxy layer. use one from a visible map canvas instead
            pass

Benjamin Jakimow's avatar
Benjamin Jakimow committed
883 884 885 886 887
        from eotimeseriesviewer.main import EOTimeSeriesViewer
        eotsv = EOTimeSeriesViewer.instance()
        if not isinstance(eotsv, EOTimeSeriesViewer):
            return
        menu = QMenu(view)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
888
        assert isinstance(mw, MapWidget)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
889
        if isinstance(currentLayer, QgsMapLayer):
890 891 892 893
            a = menu.addAction('Rename')
            a.triggered.connect(lambda *args, cidx=currentIndex: view.edit(cidx))
            menu.addSeparator()

Benjamin Jakimow's avatar
Benjamin Jakimow committed
894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913
            # zoom to layer
            menu.addAction(eotsv.actionZoomToLayer())

            # rename layer
            #

            # zoom to native resolution
            # in this case not using a map tool but the current layer
            ref = eotsv.actionZoomActualSize()
            a = menu.addAction(ref.text())
            a.setIcon(ref.icon())
            a.triggered.connect(self.onZoomActualSize)

            if isinstance(currentLayer, QgsRasterLayer):
                a = menu.addAction('&Stretch Using Current Extent')
                a.triggered.connect(self.onStretchToExtent)

            # ----
            menu.addSeparator()
            a = menu.addAction('Add Spectral Library Layer')
914
            a.triggered.connect(self.mapView().addSpectralProfileLayers)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951

            a = menu.addAction('Add Temporal Profile Layer')
            a.triggered.connect(self.mapView().addTemporalProfileLayer)

            # ----
            menu.addSeparator()
            # duplicate layer
            # remove layer

            a = menu.addAction('Remove layer')
            a.setToolTip('Remove layer(s)')
            a.triggered.connect(self.onRemoveLayers)

            menu.addSeparator()
            if isinstance(currentLayer, QgsVectorLayer):
                menu.addAction(eotsv.actionOpenTable())
                menu.addAction(eotsv.actionToggleEditing())

            if isinstance(currentLayer, QgsRasterLayer) and not isinstance(currentLayer, SensorProxyLayer):
                pass

            menu.addSeparator()
            # ----------
            # set CRS
            action = menu.addAction('Set layer CRS to map canvas')
            action.triggered.connect(self.onSetCanvasCRS)

            # ----
            # Export...
            # ------
            menu.addSeparator()
            # Styles...
            menu.addAction(eotsv.actionPasteLayerStyle())
            menu.addAction(eotsv.actionCopyLayerStyle())

            # Properties
            menu.addSeparator()
952
            menu.addAction(eotsv.actionLayerProperties())
Benjamin Jakimow's avatar
Benjamin Jakimow committed
953 954 955 956