Skip to content
Snippets Groups Projects
main.py 46.4 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.profilevisualization import SpectralTemporalVisualization
    
    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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
            self.setCentralWidget(self.mCentralWidget)
    
            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(TimeSeriesDockUI(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):
            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):
    
            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
    
    
            def onClosed():
                TimeSeriesViewer._instance = None
            self.ui.sigAboutToBeClosed.connect(onClosed)
    
    
    
    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.ui.dockTimeSeries.setTimeSeries(self.mTimeSeries)
            self.ui.dockSensors.setTimeSeries(self.mTimeSeries)
    
            self.spectralTemporalVis = SpectralTemporalVisualization(self.mTimeSeries, self.ui.dockProfiles)
    
            self.spectralTemporalVis.pixelLoader.sigLoadingFinished.connect(
                lambda dt: self.ui.dockSystemInfo.addTimeDelta('Pixel Profile', dt))
    
            assert isinstance(self, TimeSeriesViewer)
    
    
            from eotimeseriesviewer.mapvisualization import SpatialTemporalVisualization
    
            self.spatialTemporalVis = SpatialTemporalVisualization(self)
    
            self.spatialTemporalVis.sigShowProfiles.connect(self.onShowProfile)
    
            self.ui.dockMapViews.sigCrsChanged.connect(self.spatialTemporalVis.setCrs)
            self.ui.dockMapViews.sigMapSizeChanged.connect(self.spatialTemporalVis.setMapSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.dockMapViews.sigMapCanvasColorChanged.connect(self.spatialTemporalVis.setMapBackgroundColor)
    
            self.spatialTemporalVis.sigCRSChanged.connect(self.ui.dockMapViews.setCrs)
            self.spatialTemporalVis.sigMapSizeChanged.connect(self.ui.dockMapViews.setMapSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.spatialTemporalVis.sigSpatialExtentChanged.connect(self.timeSeries().setCurrentSpatialExtent)
    
            self.spatialTemporalVis.sigVisibleDatesChanged.connect(self.timeSeries().setCurrentDates)
    
            self.spectralTemporalVis.sigMoveToTSD.connect(self.setCurrentDate)
    
    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(
                lambda: self.spatialTemporalVis.setCurrentLayer(
                    self.ui.dockLabeling.labelingWidget().currentVectorSource()))
    
            initMapToolAction(self.ui.dockLabeling.labelingWidget().actionAddFeature(), MapTools.AddFeature)
    
            #initMapToolAction(self.ui.dockLabeling., MapTools.AddFeature)
    
            # set default map tool
    
            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'))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.actionAddMapView.triggered.connect(self.spatialTemporalVis.mMapViewDock.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(self.ui.dockTimeSeries.selectedTimeSeriesDates()))
    
            self.ui.actionRefresh.triggered.connect(self.spatialTemporalVis.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(self.spatialTemporalVis.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)
    
            self.spatialTemporalVis.sigMapViewAdded.connect(self.onMapViewAdded)
    
            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)->list:
            """
            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.addLayer(self.spectralTemporalVis.temporalProfileLayer())
            mapView.addLayer(self.spectralLibrary())
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def temporalProfileLayer(self)->QgsVectorLayer:
            """
            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:TimeSeriesDate):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
    
            Moves the viewport of the scroll window to a specific TimeSeriesDate
            :param tsd:  TimeSeriesDate
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
    
            assert isinstance(tsd, TimeSeriesDate)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.spatialTemporalVis.navigateToTSD(tsd)
    
            self.ui.dockTimeSeries.showTSD(tsd)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def mapCanvases(self)->list:
            """
            Returns all MapCanvases of the spatial visualization
            :return: [list-of-MapCanvases]
            """
            return self.spatialTemporalVis.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)
                self.spatialTemporalVis.setSpatialCenter(center)
    
    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 ca in m.actions():
                    assert isinstance(ca, QAction)
                    if ca == a:
                        self.ui.actionSelectFeatures.setIcon(a.icon())
                        self.ui.actionSelectFeatures.setToolTip(a.toolTip())
                    ca.setChecked(ca == a)
            self.setMapTool(MapTools.SelectFeature)
    
        def onCrosshairPositionChanged(self, spatialPoint:SpatialPoint):
            """
            Synchronizes all crosshair positions. Takes care of CRS differences.
            :param spatialPoint: SpatialPoint of the new Crosshair position
            """
            sender = self.sender()
            from .mapcanvas import MapCanvas
            for mapCanvas in self.mapCanvases():
                if isinstance(mapCanvas, MapCanvas) and mapCanvas != sender:
                    mapCanvas.setCrosshairPosition(spatialPoint, emitSignal=False)
    
    
    
        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.spatialTemporalVis.setSpatialExtent(SpatialExtent.fromMapCanvas(iface.mapCanvas())))
            self.ui.actionExportExtent.triggered.connect(lambda: iface.mapCanvas().setExtent(self.spatialTemporalVis.spatialExtent().toCrs(iface.mapCanvas().mapSettings().destinationCrs())))
            self.ui.actionExportCenter.triggered.connect(lambda: iface.mapCanvas().setCenter(self.spatialTemporalVis.spatialExtent().spatialCenter()))
            self.ui.actionImportCenter.triggered.connect(lambda: self.spatialTemporalVis.setSpatialCenter(SpatialPoint.fromMapCanvasCenter(iface.mapCanvas())))
    
    
            def onSyncRequest(qgisChanged:bool):
                if self.ui.optionSyncMapCenter.isChecked():
                    self.spatialTemporalVis.syncQGISCanvasCenter(qgisChanged)
    
            self.spatialTemporalVis.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 value 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:
    
                self.ui.mapWidget.mMapRefreshTimer.start(v)
    
    
            v = value(Keys.MapBackgroundColor)
            if isinstance(v, QColor):
    
                self.ui.dockMapViews.setMapBackgroundColor(v)
    
    
            v = value(Keys.MapSize)
            if isinstance(v, QSize):
    
                self.ui.mapWidget.setMapSize(v)
    
        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:
            """
    
            self.ui.mapWidget.setMapTool(mapToolKey, *args)
    
        def setMapSize(self, size:QSize):
            """
            Sets the MapCanvas size.
            :param size: QSize
            """
            self.spatialTemporalVis.setMapSize(size)
    
    
        def setSpatialExtent(self, spatialExtent:SpatialExtent):
    
            Sets the map canvas extent
            :param spatialExtent: SpatialExtent
    
            self.spatialTemporalVis.setSpatialExtent(spatialExtent)
    
        def setSpatialCenter(self, spatialPoint:SpatialPoint):
            """
            Sets the center of map canvases
            :param spatialPoint: SpatialPoint
            """
            self.spatialTemporalVis.setSpatialCenter(spatialPoint)
    
        def spatialCenter(self)->SpatialPoint:
            return self.spatialTemporalVis.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):
    
            # self.spatialTemporalVis.sigShowProfiles.connect(self.spectralTemporalVis.loadCoordinate)
    
            assert isinstance(spatialPoint, SpatialPoint)
            assert isinstance(mapCanvas, QgsMapCanvas)
    
            from eotimeseriesviewer.mapcanvas import MapTools
    
            assert mapToolKey in MapTools.mapToolKeys()
    
            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)
    
            elif mapToolKey == MapTools.CursorLocation:
    
    
                self.ui.dockCursorLocation.loadCursorLocation(spatialPoint, mapCanvas)
    
    
        def messageBar(self)->QgsMessageBar:
            """
            Returns the QgsMessageBar that is used to show messages in the TimeSeriesViewer UI.
            :return: QgsMessageBar
            """
    
            return self.ui.messageBar
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None):
    
            """
            Loads a time series definition file
            :param path:
            :param n_max:
            :return:
            """
    
            s = settings()
    
            if not (isinstance(path, str) and os.path.isfile(path)):
    
                defFile = s.value('file_ts_definition')
                defDir = None
                if defFile is not None:
                    defDir = os.path.dirname(defFile)
    
                filters = "CSV (*.csv *.txt);;" + \
                          "All files (*.*)"
    
                path, filter = QFileDialog.getOpenFileName(caption='Load Time Series definition', directory=defDir, filter=filters)
    
            if path is not None and os.path.exists(path):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                s.setValue('file_ts_definition', path)
    
                progressDialog = self._createProgressDialog()
    
                self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            return self.spatialTemporalVis.createMapView(name=name)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def mapViews(self)->list:
            """
            Returns all MapViews
            :return: [list-of-MapViews]
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.spatialTemporalVis.mMapViewDock[:]
    
        def icon(self)->QIcon:
            """
            Returns the EO Time Series Viewer icon