Skip to content
Snippets Groups Projects
main.py 47.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • unknown's avatar
    unknown committed
    # -*- coding: utf-8 -*-
    """
    /***************************************************************************
    
    unknown's avatar
    unknown committed
                                  -------------------
            begin                : 2015-08-20
            git sha              : $Format:%H$
    
            copyright            : (C) 2017 by HU-Berlin
            email                : benjamin.jakimow@geo.hu-berlin.de
    
    unknown's avatar
    unknown committed
     ***************************************************************************/
    
    /***************************************************************************
     *                                                                         *
     *   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
    
    File "D:\Programs\OSGeo4W\apps\Python27\lib\multiprocessing\managers.py", line
    528, in start
    self._address = reader.recv()
    EOFError
    
    see https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Multiprocessing
    see https://github.com/CleanCut/green/issues/103 
    
    path = os.path.abspath(os.path.join(sys.exec_prefix, '../../bin/pythonw.exe'))
    if os.path.exists(path):
        multiprocessing.set_executable(path)
        sys.argv = [ None ]
    
    import qgis.utils
    from qgis.core import *
    from qgis.gui import *
    
    from eotimeseriesviewer.utils import *
    from eotimeseriesviewer.timeseries import *
    
    from eotimeseriesviewer.mapcanvas import MapCanvas
    
    from eotimeseriesviewer.profilevisualization import SpectralTemporalVisualization
    
    from eotimeseriesviewer.temporalprofiles import TemporalProfileLayer
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    from eotimeseriesviewer.mapvisualization import MapView, MapWidget
    
    from eotimeseriesviewer import SpectralProfile, SpectralLibrary, SpectralLibraryPanel
    
    from eotimeseriesviewer.externals.qps.maptools import MapTools, CursorLocationMapTool, QgsMapToolSelect, QgsMapToolSelectionHandler
    
    from eotimeseriesviewer.externals.qps.cursorlocationvalue import CursorLocationInfoModel, CursorLocationInfoDock
    
    import eotimeseriesviewer.labeling
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    EXTRA_SPECLIB_FIELDS = [
        QgsField('date', QVariant.String, 'varchar'),
        QgsField('doy', QVariant.Int, 'int'),
        QgsField('sensor', QVariant.String, 'varchar')
    ]
    
    class AboutDialogUI(QDialog, loadUI('aboutdialog.ui')):
        def __init__(self, parent=None):
            """Constructor."""
            super(AboutDialogUI, self).__init__(parent)
            # Set up the user interface from Designer.
            # After setupUI you can access any designer object by doing
            # self.<objectname>, and you can use autoconnect slots - see
            # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
            # #widgets-and-dialogs-with-auto-connect
            self.setupUi(self)
    
            self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
    
            self.init()
    
        def init(self):
            self.mTitle = self.windowTitle()
            self.listWidget.currentItemChanged.connect(lambda: self.setAboutTitle())
            self.setAboutTitle()
    
            # page About
            from eotimeseriesviewer import PATH_LICENSE, __version__, PATH_CHANGELOG, PATH_ABOUT
            self.labelVersion.setText('{}'.format(__version__))
    
            def readTextFile(path):
                if os.path.isfile(path):
                    f = open(path, encoding='utf-8')
                    txt = f.read()
                    f.close()
                else:
                    txt = 'unable to read {}'.format(path)
                return txt
    
            # page Changed
            self.tbAbout.setHtml(readTextFile(PATH_ABOUT))
    
            self.tbChanges.setHtml(readTextFile(PATH_CHANGELOG + '.html'))
            self.tbLicense.setHtml(readTextFile(os.path.splitext(PATH_LICENSE)[0] + '.html'))
    
    
    
        def setAboutTitle(self, suffix=None):
            item = self.listWidget.currentItem()
    
            if item:
                title = '{} | {}'.format(self.mTitle, item.text())
            else:
                title = self.mTitle
            if suffix:
                title += ' ' + suffix
            self.setWindowTitle(title)
    
    
    
    
    
    
    class TimeSeriesViewerUI(QMainWindow,
    
                             loadUI('timeseriesviewer.ui')):
    
        def __init__(self, parent=None):
            """Constructor."""
            super(TimeSeriesViewerUI, self).__init__(parent)
            # Set up the user interface from Designer.
            # After setupUI you can access any designer object by doing
            # self.<objectname>, and you can use autoconnect slots - see
            # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
            # #widgets-and-dialogs-with-auto-connect
            self.setupUi(self)
    
            self.addActions(self.findChildren(QAction))
    
            from eotimeseriesviewer import TITLE, icon, __version__
    
            self.mInitResized = False
    
            self.mMapToolActions = []
    
            self.setWindowTitle('{} ({})'.format(TITLE, __version__))
    
            if sys.platform == 'darwin':
                self.menuBar().setNativeMenuBar(False)
    
            # set button default actions -> this will show the action icons as well
            # I don't know why this is not possible in the QDesigner when QToolButtons are
            # placed outside a toolbar
    
            def addDockWidget(dock:QDockWidget):
    
                """
                shortcut to add a created dock and return it
                :param dock:
                :return:
                """
    
                self.addDockWidget(area, dock)
                return dock
    
            area = Qt.LeftDockWidgetArea
    
    
            # self.dockRendering = addDockWidget(docks.RenderingDockUI(self))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            from eotimeseriesviewer.mapvisualization import MapViewDock
            self.dockMapViews = addDockWidget(MapViewDock(self))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # self.tabifyDockWidget(self.dockMapViews, self.dockRendering)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # self.tabifyDockWidget(self.dockSensors, self.dockCursorLocation)
    
    
            area = Qt.BottomDockWidgetArea
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # from timeseriesviewer.mapvisualization import MapViewDockUI
            # self.dockMapViews = addDockWidget(MapViewDockUI(self))
    
            self.dockTimeSeries = addDockWidget(TimeSeriesDock(self))
    
            self.dockTimeSeries.initActions(self)
    
    
            from eotimeseriesviewer.profilevisualization import ProfileViewDockUI
    
            self.dockProfiles = addDockWidget(ProfileViewDockUI(self))
    
            from eotimeseriesviewer.labeling import LabelingDock
    
            self.dockLabeling = addDockWidget(LabelingDock(self))
    
    
            area = Qt.LeftDockWidgetArea
    
            self.dockAdvancedDigitizingDockWidget = addDockWidget(QgsAdvancedDigitizingDockWidget(self.dockLabeling.labelingWidget().canvas(), self))
    
            self.dockAdvancedDigitizingDockWidget.setVisible(False)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            area = Qt.BottomDockWidgetArea
    
            self.dockSpectralLibrary = addDockWidget(panel)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.tabifyDockWidget(self.dockTimeSeries, self.dockSpectralLibrary)
    
            self.tabifyDockWidget(self.dockTimeSeries, self.dockProfiles)
    
            self.tabifyDockWidget(self.dockTimeSeries, self.dockLabeling)
    
    
            self.dockTaskManager = QgsDockWidget('Task Manager')
            self.dockTaskManager.setWidget(QgsTaskManagerWidget(QgsApplication.taskManager()))
            self.dockTaskManager.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.dockTaskManager = addDockWidget(self.dockTaskManager)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            from eotimeseriesviewer.systeminfo import SystemInfoDock
            from eotimeseriesviewer.sensorvisualization import SensorDockUI
    
            self.dockSystemInfo = addDockWidget(SystemInfoDock(self))
            self.dockSystemInfo.setVisible(False)
    
    
            self.dockSensors = addDockWidget(SensorDockUI(self))
            self.dockCursorLocation = addDockWidget(CursorLocationInfoDock(self))
    
            self.tabifyDockWidget(self.dockTaskManager, self.dockCursorLocation)
    
            self.tabifyDockWidget(self.dockTaskManager, self.dockSystemInfo)
            self.tabifyDockWidget(self.dockTaskManager, self.dockSensors)
    
            for dock in self.findChildren(QDockWidget):
    
                if len(dock.actions()) > 0:
                    s = ""
                self.menuPanels.addAction(dock.toggleViewAction())
    
    
            self.dockTimeSeries.raise_()
    
    
        def registerMapToolAction(self, a:QAction)->QAction:
            """
            Registers this action as map tools action. If triggered, all other mapt tool actions with be set unchecked
            :param a: QAction
            :return: QAction
            """
    
    
            assert isinstance(a, QAction)
            if a not in self.mMapToolActions:
                self.mMapToolActions.append(a)
            a.setCheckable(True)
    
            a.toggled.connect(lambda b, action=a: self.onMapToolActionToggled(b, action))
    
    
        def onMapToolActionToggled(self, b:bool, action:QAction):
    
            """
            Reacts on togglinga map tool
            :param b:
            :param action:
            """
    
            assert isinstance(action, QAction)
            otherActions = [a for a in self.mMapToolActions if a != action]
    
            # enable / disable the other maptool actions
            if b is True:
                for a in otherActions:
                    assert isinstance(a, QAction)
                    a.setChecked(False)
    
            else:
                otherSelected = [a for a in otherActions if a.isChecked()]
                if len(otherSelected) == 0:
                    action.setChecked(True)
    
            b = self.actionIdentify.isChecked()
            self.optionIdentifyCursorLocation.setEnabled(b)
            self.optionIdentifySpectralProfile.setEnabled(b)
            self.optionIdentifyTemporalProfile.setEnabled(b)
            self.optionMoveCenter.setEnabled(b)
    
        def closeEvent(self, a0:QCloseEvent):
            self.sigAboutToBeClosed.emit()
    
        """
        def resizeEvent(self, event:QResizeEvent):
    
            super(TimeSeriesViewerUI, self).resizeEvent(event)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if False and not self.mInitResized:
                pass
            w = 0.5
            minH = int(self.size().height() * w)
            print(minH)
            #self.mCentralWidget.setMinimumHeight(minH)
            for d in self.findChildren(QDockWidget):
                w = d.widget()
                assert isinstance(d, QDockWidget)
                print((d.objectName(), d.minimumHeight(), d.sizePolicy().verticalPolicy()))
                d.setMinimumHeight(0)
                s = ""
            #self.mCentralWidget.setMinimumWidth(int(self.size().width() * w))
                #self.mInitResized = True
        """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    LUT_MESSAGELOGLEVEL = {
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    Qgis.Info: 'INFO',
                    Qgis.Critical: 'INFO',
                    Qgis.Warning: 'WARNING',
                    Qgis.Success: 'SUCCESS',
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    def showMessage(message, title, level):
        v = QgsMessageViewer()
        v.setTitle(title)
        #print('DEBUG MSG: {}'.format(message))
        v.setMessage(message, QgsMessageOutput.MessageHtml \
            if message.startswith('<html>')
        else QgsMessageOutput.MessageText)
        v.showMessage(True)
    
    
    
    class TimeSeriesViewer(QgisInterface, QObject):
    
        _instance = None
    
        @staticmethod
        def instance():
    
            """
            Returns the TimeSeriesViewer instance
            :return:
            """
    
            return TimeSeriesViewer._instance
    
    
        sigCurrentLocationChanged = pyqtSignal([SpatialPoint],
                                               [SpatialPoint, QgsMapCanvas])
    
        sigCurrentSpectralProfilesChanged = pyqtSignal(list)
        sigCurrentTemporalProfilesChanged = pyqtSignal(list)
    
    
        def __init__(self):
    
    unknown's avatar
    unknown committed
            """Constructor.
    
            :param iface: An interface instance that will be passed to this class
                which provides the hook by which you can manipulate the QGIS
                application at run time.
            :type iface: QgsInterface
            """
    
            QObject.__init__(self)
            QgisInterface.__init__(self)
            QApplication.processEvents()
    
    
            import eotimeseriesviewer.utils
            eotimeseriesviewer.utils.MAP_LAYER_STORES.insert(0, self.mapLayerStore())
    
            self.ui = TimeSeriesViewerUI()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            mvd = self.ui.dockMapViews
            dts = self.ui.dockTimeSeries
    
            from eotimeseriesviewer.timeseries import TimeSeriesDock
            from eotimeseriesviewer.mapvisualization import MapViewDock, MapWidget
            assert isinstance(mvd, MapViewDock)
            assert isinstance(mw, MapWidget)
            assert isinstance(dts, TimeSeriesDock)
    
    
            def onClosed():
                TimeSeriesViewer._instance = None
            self.ui.sigAboutToBeClosed.connect(onClosed)
    
    
            QgsApplication.instance().messageLog().messageReceived.connect(self.logMessage)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # Save reference to the QGIS interface
    
            import qgis.utils
    
            assert isinstance(qgis.utils.iface, QgisInterface)
    
            # init empty time series
    
            self.mTimeSeries.setDateTimePrecision(DateTimePrecision.Day)
    
            self.mTimeSeries.sigTimeSeriesDatesAdded.connect(self.onTimeSeriesChanged)
    
            self.mTimeSeries.sigTimeSeriesDatesRemoved.connect(self.onTimeSeriesChanged)
    
            dts.setTimeSeries(self.mTimeSeries)
    
            self.ui.dockSensors.setTimeSeries(self.mTimeSeries)
    
            mw.setTimeSeries(self.mTimeSeries)
            mvd.setTimeSeries(self.mTimeSeries)
            mvd.setMapWidget(mw)
    
    
            self.spectralTemporalVis = SpectralTemporalVisualization(self.mTimeSeries, self.ui.dockProfiles)
    
            assert isinstance(self, TimeSeriesViewer)
    
            self.spectralTemporalVis.sigMoveToDate.connect(self.setCurrentDate)
    
            mw.sigSpatialExtentChanged.connect(self.timeSeries().setCurrentSpatialExtent)
            mw.sigVisibleDatesChanged.connect(self.timeSeries().setVisibleDates)
            mw.sigMapViewAdded.connect(self.onMapViewAdded)
    
            mw.sigCurrentLocationChanged.connect(self.setCurrentLocation)
    
    
            tb = self.ui.toolBarTimeControl
            assert isinstance(tb, QToolBar)
            tb.addAction(mw.actionFirstDate)
            tb.addAction(mw.actionBackwardFast)
            tb.addAction(mw.actionBackward)
            tb.addAction(mw.actionForward)
            tb.addAction(mw.actionForwardFast)
            tb.addAction(mw.actionLastDate)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            tstv = self.ui.dockTimeSeries.timeSeriesTreeView
            assert isinstance(tstv, TimeSeriesTreeView)
    
            tstv.sigMoveToDateRequest.connect(self.setCurrentDate)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.mCurrentMapLocation = None
            self.mCurrentMapSpectraLoading = 'TOP'
    
    
            self.ui.actionLockMapPanelSize.toggled.connect(self.lockCentralWidgetSize)
    
    
            def initMapToolAction(action, key):
                assert isinstance(action, QAction)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                assert isinstance(key, MapTools)
    
    
                action.triggered.connect(lambda: self.setMapTool(key))
                action.setProperty('eotsv/maptoolkey', key)
    
                self.ui.registerMapToolAction(action)
    
    
            initMapToolAction(self.ui.actionPan, MapTools.Pan)
            initMapToolAction(self.ui.actionZoomIn, MapTools.ZoomIn)
            initMapToolAction(self.ui.actionZoomOut, MapTools.ZoomOut)
            initMapToolAction(self.ui.actionZoomPixelScale, MapTools.ZoomPixelScale)
            initMapToolAction(self.ui.actionZoomFullExtent, MapTools.ZoomFull)
            initMapToolAction(self.ui.actionIdentify, MapTools.CursorLocation)
    
    
            initMapToolAction(self.ui.actionSelectFeatures, MapTools.SelectFeature)
            assert isinstance(self.ui.actionSelectFeatures, QAction)
    
            self.ui.optionSelectFeaturesRectangle.triggered.connect(self.onSelectFeatureOptionTriggered)
            self.ui.optionSelectFeaturesPolygon.triggered.connect(self.onSelectFeatureOptionTriggered)
            self.ui.optionSelectFeaturesFreehand.triggered.connect(self.onSelectFeatureOptionTriggered)
            self.ui.optionSelectFeaturesRadius.triggered.connect(self.onSelectFeatureOptionTriggered)
    
            m = QMenu()
            m.addAction(self.ui.optionSelectFeaturesRectangle)
            m.addAction(self.ui.optionSelectFeaturesPolygon)
            m.addAction(self.ui.optionSelectFeaturesFreehand)
            m.addAction(self.ui.optionSelectFeaturesRadius)
    
            self.ui.actionSelectFeatures.setMenu(m)
    
            tb = self.ui.toolBarVectorFeatures
    
            assert isinstance(tb, QToolBar)
    
            tb.addAction(self.ui.dockLabeling.labelingWidget().actionToggleEditing())
            tb.addAction(self.ui.dockLabeling.labelingWidget().actionSaveEdits())
            tb.addAction(self.ui.dockLabeling.labelingWidget().actionAddFeature())
    
            labelingWidget = self.ui.dockLabeling.labelingWidget()
            from .labeling import LabelingWidget
            assert isinstance(labelingWidget, LabelingWidget)
            labelingWidget.sigMapExtentRequested.connect(self.setSpatialExtent)
            labelingWidget.sigMapCenterRequested.connect(self.setSpatialCenter)
            labelingWidget.sigVectorLayerChanged.connect(
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                lambda: self.mapWidget().setCurrentLayer(
    
                    self.ui.dockLabeling.labelingWidget().currentVectorSource()))
    
            initMapToolAction(self.ui.dockLabeling.labelingWidget().actionAddFeature(), MapTools.AddFeature)
    
            self.ui.actionPan.toggle()
    
            self.ui.dockCursorLocation.sigLocationRequest.connect(self.ui.actionIdentifyCursorLocationValues.trigger)
    
            self.ui.dockCursorLocation.mLocationInfoModel.setNodeExpansion(CursorLocationInfoModel.ALWAYS_EXPAND)
    
    
            # D.actionIdentifyMapLayers.triggered.connect(lambda: self.spatialTemporalVis.activateMapTool('identifyMapLayers'))
    
            self.ui.actionAddMapView.triggered.connect(mvd.createMapView)
    
            self.ui.actionAddTSD.triggered.connect(lambda: self.addTimeSeriesImages(None))
            self.ui.actionAddVectorData.triggered.connect(lambda: self.addVectorData())
    
            self.ui.actionRemoveTSD.triggered.connect(lambda: self.mTimeSeries.removeTSDs(dts.selectedTimeSeriesDates()))
            self.ui.actionRefresh.triggered.connect(mw.refresh)
    
            self.ui.actionLoadTS.triggered.connect(self.loadTimeSeriesDefinition)
            self.ui.actionClearTS.triggered.connect(self.clearTimeSeries)
            self.ui.actionSaveTS.triggered.connect(self.saveTimeSeriesDefinition)
    
            self.ui.actionAddTSExample.triggered.connect(lambda: self.loadExampleTimeSeries(loadAsync=False))
    
            self.ui.actionLoadTimeSeriesStack.triggered.connect(self.loadTimeSeriesStack)
    
            #self.ui.actionShowCrosshair.toggled.connect(mw.setCrosshairVisibility)
    
            self.ui.actionExportMapsToImages.triggered.connect(lambda: self.exportMapsToImages())
    
            self.spectralTemporalVis.ui.actionLoadProfileRequest.triggered.connect(self.activateIdentifyTemporalProfileMapTool)
            self.ui.dockSpectralLibrary.SLW.actionSelectProfilesFromMap.triggered.connect(self.activateIdentifySpectralProfileMapTool)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # connect buttons with actions
    
            self.ui.actionAbout.triggered.connect(lambda: AboutDialogUI(self.ui).exec_())
    
    
            self.ui.actionSettings.triggered.connect(self.onShowSettingsDialog)
    
            import webbrowser
    
            from eotimeseriesviewer import DOCUMENTATION, SpectralLibrary, SpectralLibraryPanel, SpectralLibraryWidget
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.actionShowOnlineHelp.triggered.connect(lambda: webbrowser.open(DOCUMENTATION))
    
            SLW = self.ui.dockSpectralLibrary.spectralLibraryWidget()
            assert isinstance(SLW, SpectralLibraryWidget)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            SLW.setMapInteraction(True)
            SLW.setCurrentProfilesMode(SpectralLibraryWidget.CurrentProfilesMode.automatically)
            SLW.sigMapExtentRequested.connect(self.setSpatialExtent)
            SLW.sigMapCenterRequested.connect(self.setSpatialCenter)
    
    
            # add time-specific fields
            sl = self.spectralLibrary()
    
            assert isinstance(sl, SpectralLibrary)
            sl.setName('EOTS Spectral Library')
            sl.startEditing()
            for field in EXTRA_SPECLIB_FIELDS:
                sl.addAttribute(field)
            assert sl.commitChanges()
    
            self.mMapLayerStore.addMapLayer(sl)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            temporalProfileLayer = self.spectralTemporalVis.temporalProfileLayer()
            assert isinstance(temporalProfileLayer, QgsVectorLayer)
            temporalProfileLayer.setName('EOTS Temporal Profiles')
    
            self.mMapLayerStore.addMapLayer(temporalProfileLayer)
    
            eotimeseriesviewer.labeling.MAP_LAYER_STORES.append(self.mMapLayerStore)
            eotimeseriesviewer.labeling.registerLabelShortcutEditorWidget()
    
            self.applySettings()
    
    
            for toolBar in self.ui.findChildren(QToolBar):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.dockTimeSeries.setFloating(True)
            self.ui.dockTimeSeries.setFloating(False)
    
    
        def lockCentralWidgetSize(self, b:bool):
            """
            Locks or release the current central widget size
            :param b:
            """
            w = self.ui.centralWidget()
    
            size = w.size()
            if b:
                w.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
                w.setMinimumSize(size)
            else:
                w.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
                w.setMinimumSize(0, 0)
    
    
        def sensors(self)->typing.List[SensorInstrument]:
    
            """
            Returns the list of Sensors
            :return: [list-of-Sensors]
            """
            return self.mTimeSeries.sensors()
    
    
    
        def activateIdentifyTemporalProfileMapTool(self, *args):
            """
            Activates the collection of temporal profiles
            """
            self.ui.actionIdentify.trigger()
            self.ui.optionIdentifyTemporalProfile.setChecked(True)
    
        def activateIdentifySpectralProfileMapTool(self, *args):
            """
            Activates the collection of spectral profiles
            """
            self.ui.actionIdentify.trigger()
            self.ui.optionIdentifySpectralProfile.setChecked(True)
    
    
        def _createProgressDialog(self, title='Load Data')->QProgressDialog:
            """
            Creates a QProgressDialog to load image data
            :return: QProgressDialog
            """
            progressDialog = QProgressDialog(self.ui)
            progressDialog.setWindowTitle(title)
            progressDialog.setMinimumDuration(500)
            progressDialog.setValue(0)
            progressDialog.setWindowFlags(progressDialog.windowFlags() & ~Qt.WindowContextHelpButtonHint)
            return progressDialog
    
    
        def exportMapsToImages(self, path=None, format='PNG'):
            """
            Exports the map canvases to local images.
            :param path: directory to save the images in
            :param format: rastr format, e.g. 'PNG' or 'JPG'
            """
    
            from .mapcanvas import MapCanvas
            from .mapvisualization import MapView
            from .settings import Keys, setValue, value
            import string
    
    
            if path is None:
                d = SaveAllMapsDialog()
    
                path = value(Keys.MapImageExportDirectory, default=None)
                if isinstance(path, str):
                    d.setDirectory(path)
    
                if d.exec() != QDialog.Accepted:
                    s = ""
                    return
    
                format = d.fileType().lower()
                path = d.directory()
    
    
    
            else:
                format = format.lower()
    
            mapCanvases = self.mapCanvases()
            n = len(mapCanvases)
    
            progressDialog = self._createProgressDialog(title='Save Map Images...')
    
            progressDialog.setRange(0, n)
    
            valid_chars = "-_.() {}{}".format(string.ascii_letters, string.digits)
    
            for i, mapCanvas in enumerate(mapCanvases):
                if progressDialog.wasCanceled():
                    return
    
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.timedRefresh()
                tsd = mapCanvas.tsd()
                mv = mapCanvas.mapView()
                assert isinstance(mv, MapView)
                mapCanvas.waitWhileRendering()
                imgPath = '{}.{}.{}'.format(tsd.date(), mv.title(), format)
    
                imgPath = ''.join(c for c in imgPath if c in valid_chars)
                imgPath = imgPath.replace(' ', '_')
                imgPath = os.path.join(path, imgPath)
    
                mapCanvas.saveAsImage(imgPath, None, format)
                progressDialog.setValue(i + 1)
                progressDialog.setLabelText('{}/{} maps saved'.format(i+1, n))
    
                if progressDialog.wasCanceled():
                    return
    
            setValue(Keys.MapImageExportDirectory, path)
    
    
    
        def onMapViewAdded(self, mapView:MapView):
            """
    
            :param mapView:
            :return:
            """
    
            mapView.addLayer(self.spectralTemporalVis.temporalProfileLayer())
            mapView.addLayer(self.spectralLibrary())
    
    
        def temporalProfileLayer(self)->TemporalProfileLayer:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Returns the TemporalProfileLayer
            :return:
            """
            from eotimeseriesviewer.profilevisualization import SpectralTemporalVisualization
            return self.spectralTemporalVis.temporalProfileLayer()
    
    
    
        def spectralLibrary(self)->SpectralLibrary:
            """
            Returns the SpectraLibrary of the SpectralLibrary dock
            :return: SpectraLibrary
            """
    
            from .externals.qps.speclib.spectrallibraries import SpectralLibraryPanel
    
            if isinstance(self.ui.dockSpectralLibrary, SpectralLibraryPanel):
                return self.ui.dockSpectralLibrary.SLW.speclib()
            else:
                return None
    
        def actionZoomActualSize(self):
            return self.ui.actionZoomPixelScale
    
        def actionZoomFullExtent(self):
            return self.ui.actionZoomFullExtent
    
        def actionZoomIn(self):
            return self.ui.actionZoomIn
    
        def actionZoomOut(self):
            return self.ui.actionZoomOut
    
    
        def setCurrentDate(self, tsd):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
    
            Moves the viewport of the scroll window to a specific TimeSeriesDate
    
            :param tsd:  TimeSeriesDate or numpy.datetime64
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
    
            tsd = self.timeSeries().findDate(tsd)
            if isinstance(tsd, TimeSeriesDate):
                self.ui.mMapWidget.setCurrentDate(tsd)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def mapCanvases(self)->typing.List[MapCanvas]:
    
            """
            Returns all MapCanvases of the spatial visualization
            :return: [list-of-MapCanvases]
            """
    
        def mapLayerStore(self)->QgsMapLayerStore:
            """
            Returns the QgsMapLayerStore which is used to register QgsMapLayers
            :return: QgsMapLayerStore
            """
            return self.mMapLayerStore
    
    
        def onMoveToFeature(self, layer:QgsMapLayer, feature:QgsFeature):
    
            """
            Move the spatial center of map visualization to `feature`.
            :param layer: QgsMapLayer
            :param feature: QgsFeature
            """
    
            g = feature.geometry()
            if isinstance(g, QgsGeometry):
                c = g.centroid()
                x, y = c.asPoint()
                crs = layer.crs()
                center = SpatialPoint(crs, x, y)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                self.ui.actionRefresh.trigger()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def onSelectFeatureOptionTriggered(self):
    
            a = self.sender()
            m = self.ui.actionSelectFeatures.menu()
    
            if isinstance(a, QAction) and isinstance(m, QMenu) and a in m.actions():
    
                for option in m.actions():
                    assert isinstance(option, QAction)
                    if option == a:
    
                        self.ui.actionSelectFeatures.setIcon(a.icon())
                        self.ui.actionSelectFeatures.setToolTip(a.toolTip())
    
                    option.setChecked(option == a)
            self.onSelectFeatureTriggered()
    
        def onSelectFeatureTriggered(self):
    
            self.setMapTool(MapTools.SelectFeature)
    
        def initQGISConnection(self):
            """
            Initializes interactions between TimeSeriesViewer and the QGIS instances
            :return:
            """
    
    
            iface = qgis.utils.iface
            assert isinstance(iface, QgisInterface)
    
    
            self.ui.actionImportExtent.triggered.connect(lambda: self.setSpatialExtent(SpatialExtent.fromMapCanvas(iface.mapCanvas())))
            self.ui.actionExportExtent.triggered.connect(lambda: iface.mapCanvas().setExtent(self.spatialExtent().toCrs(iface.mapCanvas().mapSettings().destinationCrs())))
            self.ui.actionExportCenter.triggered.connect(lambda: iface.mapCanvas().setCenter(self.spatialCenter().toCrs(iface.mapCanvas().mapSettings().destinationCrs())))
            self.ui.actionImportCenter.triggered.connect(lambda: self.setSpatialCenter(SpatialPoint.fromMapCanvasCenter(iface.mapCanvas())))
    
            def onSyncRequest(qgisChanged:bool):
                if self.ui.optionSyncMapCenter.isChecked():
    
                    self.ui.mMapWidget.syncQGISCanvasCenter(qgisChanged)
    
            self.ui.mMapWidget.sigSpatialExtentChanged.connect(lambda: onSyncRequest(False))
    
            iface.mapCanvas().extentsChanged.connect(lambda: onSyncRequest(True))
    
        def onShowSettingsDialog(self):
    
            from eotimeseriesviewer.settings import SettingsDialog
    
            d = SettingsDialog(self.ui)
            r = d.exec_()
    
            if r == QDialog.Accepted:
                self.applySettings()
                s = ""
            else:
                pass
                s  =""
    
        def applySettings(self):
            """
    
            Reads the QSettings object and applies its values to related widget components
    
            from eotimeseriesviewer.settings import value, Keys, defaultValues, setValue
    
                if value(key) == None and key in defaults.keys():
    
            v = value(Keys.DateTimePrecision)
            if isinstance(v, DateTimePrecision):
                self.mTimeSeries.setDateTimePrecision(v)
    
            v = value(Keys.MapUpdateInterval)
            if isinstance(v, int) and v > 0:
    
    
            v = value(Keys.MapBackgroundColor)
            if isinstance(v, QColor):
    
                self.ui.dockMapViews.setMapBackgroundColor(v)
    
            v = value(Keys.MapTextFormat)
            if isinstance(v, QgsTextFormat):
                self.ui.dockMapViews.setMapTextFormat(v)
    
            v = value(Keys.MapSize)
            if isinstance(v, QSize):
    
        def setMapTool(self, mapToolKey, *args, **kwds):
            """
            Sets the active QgsMapTool for all canvases know to the EOTSV.
            :param mapToolKey: str, see MapTools documentation
            :param args:
            :param kwds:
            :return:
            """
    
    
            if mapToolKey == MapTools.SelectFeature:
                if self.ui.optionSelectFeaturesRectangle.isChecked():
                    mapToolKey = MapTools.SelectFeature
                elif self.ui.optionSelectFeaturesPolygon.isChecked():
                    mapToolKey = MapTools.SelectFeatureByPolygon
                elif self.ui.optionSelectFeaturesFreehand.isChecked():
                    mapToolKey = MapTools.SelectFeatureByFreehand
                elif self.ui.optionSelectFeaturesRadius.isChecked():
                    mapToolKey = MapTools.SelectFeatureByRadius
    
    
            self.ui.mMapWidget.setMapTool(mapToolKey, *args)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setMapsPerMapView(self, n:int):
            """
            Sets the number of map canvases that is shown per map view
            :param n: int
            """
            self.mapWidget().setMapsPerMapView(n)
    
        def setMapSize(self, size:QSize):
            """
            Sets the MapCanvas size.
            :param size: QSize
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mapWidget().setMapSize(size)
    
    
        def setSpatialExtent(self, spatialExtent:SpatialExtent):
    
            Sets the map canvas extent
            :param spatialExtent: SpatialExtent
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mapWidget().setSpatialExtent(spatialExtent)
    
        def setSpatialCenter(self, spatialPoint:SpatialPoint):
            """
            Sets the center of map canvases
            :param spatialPoint: SpatialPoint
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mapWidget().setSpatialCenter(spatialPoint)
    
        def spatialExtent(self)->SpatialExtent:
            """
            Returns the map extent
            :return: SpatialExtent
            """
            return self.mapWidget().spatialExtent()
    
    
        def spatialCenter(self)->SpatialPoint:
    
            """
            Returns the map center
            :return: SpatialPoint
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.mapWidget().spatialCenter()
    
    
        def setCurrentLocation(self, spatialPoint:SpatialPoint, mapCanvas:QgsMapCanvas=None):
            """
            Sets the current "last selected" location, for which different properties might get derived,
            like cursor location values and SpectraProfiles.
            :param spatialPoint: SpatialPoint
            :param mapCanvas: QgsMapCanvas (optional), the canvas on which the location got selected
            """
            assert isinstance(spatialPoint, SpatialPoint)
    
            bCLV = self.ui.optionIdentifyCursorLocation.isChecked()
            bSP = self.ui.optionIdentifySpectralProfile.isChecked()
            bTP = self.ui.optionIdentifyTemporalProfile.isChecked()
            bCenter = self.ui.optionMoveCenter.isChecked()
    
            self.mCurrentMapLocation = spatialPoint
    
            if isinstance(mapCanvas, QgsMapCanvas):
                self.sigCurrentLocationChanged[SpatialPoint, QgsMapCanvas].emit(self.mCurrentMapLocation, mapCanvas)
    
                if bCLV:
                    self.loadCursorLocationValueInfo(spatialPoint, mapCanvas)
    
                if bCenter:
    
                    mapCanvas.setCenter(spatialPoint.toCrs(mapCanvas.mapSettings().destinationCrs()))
    
    
                if bSP:
                    self.loadCurrentSpectralProfile(spatialPoint, mapCanvas)
    
            if bTP:
                self.loadCurrentTemporalProfile(spatialPoint)
    
    
            self.sigCurrentLocationChanged[SpatialPoint].emit(self.mCurrentMapLocation)
    
        @pyqtSlot(SpatialPoint, QgsMapCanvas)
        def loadCursorLocationValueInfo(self, spatialPoint:SpatialPoint, mapCanvas:QgsMapCanvas):
            self.ui.dockCursorLocation.loadCursorLocation(spatialPoint, mapCanvas)
    
    
    
        @pyqtSlot(SpatialPoint, QgsMapCanvas)
        def loadCurrentSpectralProfile(self, spatialPoint: SpatialPoint, mapCanvas: QgsMapCanvas):
            """
            Loads SpectralProfiles from a location defined by `spatialPoint`
            :param spatialPoint: SpatialPoint
            :param mapCanvas: QgsMapCanvas
            """
            assert self.mCurrentMapSpectraLoading in ['TOP', 'ALL']
            assert isinstance(spatialPoint, SpatialPoint)
            from .mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
            tsd = mapCanvas.tsd()
    
            sensorLayers   = [l for l in mapCanvas.layers() if isinstance(l, SensorProxyLayer)]
            currentSpectra = []
    
    
            sl = self.spectralLibrary()
            for lyr in sensorLayers:
                assert isinstance(lyr, SensorProxyLayer)
                p = SpectralProfile.fromRasterLayer(lyr, spatialPoint)
                if isinstance(p, SpectralProfile):
                    p2 = p.copyFieldSubset(sl.fields())
                    p2.setName('{} {}'.format(p.name(), tsd.date()))
                    p2.setAttribute('date', '{}'.format(tsd.date()))
                    p2.setAttribute('doy', int(tsd.doy()))
                    p2.setAttribute('sensor', tsd.sensor().name())
                    currentSpectra.append(p2)
                    if self.mCurrentMapSpectraLoading == 'TOP':
                        break
    
            self.ui.dockSpectralLibrary.SLW.setCurrentSpectra(currentSpectra)
    
    
        @pyqtSlot(SpatialPoint)
        def loadCurrentTemporalProfile(self, spatialPoint: SpatialPoint):
            self.spectralTemporalVis.loadCoordinate(spatialPoint)
    
        def onShowProfile(self, spatialPoint, mapCanvas, mapToolKey):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            assert isinstance(spatialPoint, SpatialPoint)
            assert isinstance(mapCanvas, QgsMapCanvas)
    
            from eotimeseriesviewer.mapcanvas import MapTools
    
            assert mapToolKey in MapTools.mapToolValues()
    
    
            if mapToolKey == MapTools.TemporalProfile:
    
                self.spectralTemporalVis.loadCoordinate(spatialPoint)
    
            elif mapToolKey == MapTools.SpectralProfile:
    
                tsd = None
                from .mapcanvas import MapCanvas
                if isinstance(mapCanvas, MapCanvas):
                    tsd = mapCanvas.tsd()
    
                if not hasattr(self, 'cntSpectralProfile'):
                    self.cntSpectralProfile = 0
    
                profiles = SpectralProfile.fromMapCanvas(mapCanvas, spatialPoint)
    
                # add metadata
    
                if isinstance(tsd, TimeSeriesDate):
    
                    profiles2 = []
                    sl = self.spectralLibrary()
    
                    if isinstance(sl, SpectralLibrary):
                        for p in profiles:
                            self.cntSpectralProfile += 1
                            assert isinstance(p, SpectralProfile)
                            p2 = p.copyFieldSubset(fields=sl.fields())
                            p2.setName('Profile {} {}'.format(self.cntSpectralProfile, tsd.mDate))
                            p2.setAttribute('date', '{}'.format(tsd.mDate))
                            p2.setAttribute('doy', int(tsd.mDOY))
                            p2.setAttribute('sensor', tsd.mSensor.name())
                            profiles2.append(p2)
                        self.ui.dockSpectralLibrary.SLW.setCurrentSpectra(profiles2)