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

Benjamin Jakimow
committed
EO Time Series Viewer
copyright : (C) 2017 by HU-Berlin
email : benjamin.jakimow@geo.hu-berlin.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""
# noinspection PyPep8Naming

benjamin.jakimow@geo.hu-berlin.de
committed
File "D:\Programs\OSGeo4W\apps\Python27\lib\multiprocessing\managers.py", line
528, in start
self._address = reader.recv()
EOFError

benjamin.jakimow@geo.hu-berlin.de
committed
see https://github.com/pyinstaller/pyinstaller/wiki/Recipe-Multiprocessing
see https://github.com/CleanCut/green/issues/103

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

benjamin.jakimow@geo.hu-berlin.de
committed
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 *

benjamin.jakimow@geo.hu-berlin.de
committed
import qgis.utils
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
EXTRA_SPECLIB_FIELDS = [
QgsField('date', QVariant.String, 'varchar'),
QgsField('doy', QVariant.Int, 'int'),
QgsField('sensor', QVariant.String, 'varchar')
]

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

Benjamin Jakimow
committed
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)

Benjamin Jakimow
committed
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))

Benjamin Jakimow
committed
self.tbChanges.setHtml(readTextFile(PATH_CHANGELOG + '.html'))
self.tbLicense.setHtml(readTextFile(os.path.splitext(PATH_LICENSE)[0] + '.html'))

Benjamin Jakimow
committed
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')):
sigAboutToBeClosed = pyqtSignal()
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.mMapToolActions = []
self.setWindowTitle('{} ({})'.format(TITLE, __version__))
self.setWindowIcon(icon())
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
area = None
def addDockWidget(dock:QDockWidget):
"""
shortcut to add a created dock and return it
:param dock:
:return:
"""
dock.setParent(self)
self.addDockWidget(area, dock)
return dock
area = Qt.LeftDockWidgetArea
# self.dockRendering = addDockWidget(docks.RenderingDockUI(self))
from eotimeseriesviewer.mapvisualization import MapViewDock
self.dockMapViews = addDockWidget(MapViewDock(self))
# self.tabifyDockWidget(self.dockMapViews, self.dockRendering)
# self.tabifyDockWidget(self.dockSensors, self.dockCursorLocation)
area = Qt.BottomDockWidgetArea
# 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))

Benjamin Jakimow
committed
self.dockAdvancedDigitizingDockWidget = addDockWidget(QgsAdvancedDigitizingDockWidget(self.dockLabeling.labelingWidget().canvas(), self))
self.dockAdvancedDigitizingDockWidget.setVisible(False)
panel = SpectralLibraryPanel(self)
self.dockSpectralLibrary = addDockWidget(panel)
self.tabifyDockWidget(self.dockTimeSeries, self.dockSpectralLibrary)
self.tabifyDockWidget(self.dockTimeSeries, self.dockProfiles)

Benjamin Jakimow
committed
self.tabifyDockWidget(self.dockTimeSeries, self.dockLabeling)

benjamin.jakimow@geo.hu-berlin.de
committed
area = Qt.RightDockWidgetArea
self.dockTaskManager = QgsDockWidget('Task Manager')
self.dockTaskManager.setWidget(QgsTaskManagerWidget(QgsApplication.taskManager()))
self.dockTaskManager.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
self.dockTaskManager = addDockWidget(self.dockTaskManager)

benjamin.jakimow@geo.hu-berlin.de
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())
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()
Qgis.Info: 'INFO',
Qgis.Critical: 'INFO',
Qgis.Warning: 'WARNING',
Qgis.Success: 'SUCCESS',

benjamin.jakimow@geo.hu-berlin.de
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)
sigCloses = pyqtSignal()
"""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()

Benjamin Jakimow
committed
self.mMapLayerStore = QgsMapLayerStore()
import eotimeseriesviewer.utils
eotimeseriesviewer.utils.MAP_LAYER_STORES.insert(0, self.mapLayerStore())

Benjamin Jakimow
committed
TimeSeriesViewer._instance = self
self.ui = TimeSeriesViewerUI()
def onClosed():
TimeSeriesViewer._instance = None
self.ui.sigAboutToBeClosed.connect(onClosed)
assert isinstance(qgis.utils.iface, QgisInterface)

Benjamin Jakimow
committed
# init empty time series
self.mTimeSeries = TimeSeries()

Benjamin Jakimow
committed
self.mTimeSeries.setDateTimePrecision(DateTimePrecision.Day)

benjamin.jakimow@geo.hu-berlin.de
committed
self.mSpatialMapExtentInitialized = False
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)

benjamin.jakimow@geo.hu-berlin.de
committed
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)

Benjamin Jakimow
committed
self.ui.dockMapViews.sigCrsChanged.connect(self.spatialTemporalVis.setCrs)
self.ui.dockMapViews.sigMapSizeChanged.connect(self.spatialTemporalVis.setMapSize)
self.ui.dockMapViews.sigMapCanvasColorChanged.connect(self.spatialTemporalVis.setMapBackgroundColor)

Benjamin Jakimow
committed
self.spatialTemporalVis.sigCRSChanged.connect(self.ui.dockMapViews.setCrs)
self.spatialTemporalVis.sigMapSizeChanged.connect(self.ui.dockMapViews.setMapSize)
self.spatialTemporalVis.sigSpatialExtentChanged.connect(self.timeSeries().setCurrentSpatialExtent)
self.spatialTemporalVis.sigVisibleDatesChanged.connect(self.timeSeries().setCurrentDates)
self.spectralTemporalVis.sigMoveToTSD.connect(self.showTimeSeriesDate)
tstv = self.ui.dockTimeSeries.timeSeriesTreeView
assert isinstance(tstv, TimeSeriesTreeView)
tstv.sigMoveToDateRequest.connect(self.showTimeSeriesDate)
self.mCurrentMapLocation = None
self.mCurrentMapSpectraLoading = 'TOP'
def initMapToolAction(action, key):
assert isinstance(action, QAction)
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)
# create edit toolbar
tb = self.ui.toolBarEditing
assert isinstance(tb, QToolBar)

Benjamin Jakimow
committed
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()))

Benjamin Jakimow
committed
initMapToolAction(self.ui.dockLabeling.labelingWidget().actionAddFeature(), MapTools.AddFeature)
#initMapToolAction(self.ui.dockLabeling., MapTools.AddFeature)
# set default map tool
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(self.spatialTemporalVis.MVC.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())

Benjamin Jakimow
committed
self.spectralTemporalVis.ui.actionLoadProfileRequest.triggered.connect(self.activateIdentifyTemporalProfileMapTool)
self.ui.dockSpectralLibrary.SLW.actionSelectProfilesFromMap.triggered.connect(self.activateIdentifySpectralProfileMapTool)

Benjamin Jakimow
committed
self.ui.actionAbout.triggered.connect(lambda: AboutDialogUI(self.ui).exec_())
self.ui.actionSettings.triggered.connect(self.onShowSettingsDialog)
from eotimeseriesviewer import DOCUMENTATION, SpectralLibrary, SpectralLibraryPanel, SpectralLibraryWidget
self.ui.actionShowOnlineHelp.triggered.connect(lambda: webbrowser.open(DOCUMENTATION))
SLW = self.ui.dockSpectralLibrary.spectralLibraryWidget()
assert isinstance(SLW, SpectralLibraryWidget)
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)
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.initQGISConnection()
for toolBar in self.ui.findChildren(QToolBar):
for toolButton in toolBar.findChildren(QToolButton):
assert isinstance(toolButton, QToolButton)
if isinstance(toolButton.defaultAction(), QAction) and isinstance(toolButton.defaultAction().menu(), QMenu):
toolButton.setPopupMode(QToolButton.MenuButtonPopup)
self.ui.dockTimeSeries.setFloating(True)
self.ui.dockTimeSeries.setFloating(False)

Benjamin Jakimow
committed
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'
"""
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
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...')
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
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())
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

Benjamin Jakimow
committed
def showTimeSeriesDate(self, tsd:TimeSeriesDate):
Moves the viewport of the scroll window to a specific TimeSeriesDate
:param tsd: TimeSeriesDate
assert isinstance(tsd, TimeSeriesDate)

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)
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)

Benjamin Jakimow
committed
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))
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
"""

Benjamin Jakimow
committed
from eotimeseriesviewer.settings import value, Keys, defaultValues, setValue

Benjamin Jakimow
committed
# the default values

Benjamin Jakimow
committed
defaults = defaultValues()
for key in list(Keys):
if value(key) == None and key in defaults.keys():

Benjamin Jakimow
committed
setValue(key, defaults[key])
v = value(Keys.DateTimePrecision)
if isinstance(v, DateTimePrecision):
self.mTimeSeries.setDateTimePrecision(v)
v = value(Keys.MapUpdateInterval)
if isinstance(v, int) and v > 0:
self.spatialTemporalVis.mMapRefreshTimer.start(v)
v = value(Keys.MapBackgroundColor)
if isinstance(v, QColor):
self.spatialTemporalVis.setMapBackgroundColor(v)
v = value(Keys.MapSize)
if isinstance(v, QSize):
self.spatialTemporalVis.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.spatialTemporalVis.setMapTool(mapToolKey)

Benjamin Jakimow
committed
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)
831
832
833
834
835
836
837
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
@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)
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)
else:
s = ""
pass
def messageBar(self)->QgsMessageBar:
"""
Returns the QgsMessageBar that is used to show messages in the TimeSeriesViewer UI.
:return: QgsMessageBar
"""
return self.ui.messageBar
def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None):
"""
Loads a time series definition file
:param path:
:param n_max:
:return:
"""
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):
self.clearTimeSeries()
progressDialog = self._createProgressDialog()
self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
def createMapView(self, name:str=None):

Benjamin Jakimow
committed
Creates a new MapView.
:return: MapView

Benjamin Jakimow
committed
return self.spatialTemporalVis.createMapView(name=name)
def mapViews(self)->list:
"""
Returns all MapViews
:return: [list-of-MapViews]
"""
return self.spatialTemporalVis.MVC[:]
def zoomTo(self, key):
ext = self.mTimeSeries.maxSpatialExtent(self.ui.dockRendering.crs())
elif key == 'zoomPixelScale':
extent = self.spatialTemporalVis.spatialExtent()

benjamin.jakimow@geo.hu-berlin.de
committed
#calculate in web-mercator for metric distances
crs = self.spatialTemporalVis.crs()
crsWMC = QgsCoordinateReferenceSystem('EPSG:3857')
extentWMC = extent.toCrs(crsWMC)
pxSize = max(self.mTimeSeries.pixelSizes(), key= lambda s :s.width())
canvasSize = self.spatialTemporalVis.mapSize()
f = 0.05
width = f * canvasSize.width() * pxSize.width() # width in map units
height = f * canvasSize.height() * pxSize.height()

benjamin.jakimow@geo.hu-berlin.de
committed
ext = SpatialExtent(crsWMC, 0, 0, width, height)
ext.setCenter(extentWMC.center())
#return to original CRS
ext = ext.toCrs(crs)
else:
raise NotImplementedError(key)
self.spatialTemporalVis.setSpatialExtent(ext)
def icon(self)->QIcon:
"""
Returns the EO Time Series Viewer icon
:return: QIcon
"""
import eotimeseriesviewer
return eotimeseriesviewer.icon()
def logMessage(self, message, tag, level):
m = message.split('\n')
if '' in message.split('\n'):
m = m[0:m.index('')]
m = '\n'.join(m)

benjamin.jakimow@geo.hu-berlin.de
committed
if DEBUG: print(message)