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.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')):
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):
"""
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))
from eotimeseriesviewer.labeling import LabelingDock
self.dockLabeling = addDockWidget(LabelingDock(self))
self.dockLabeling.setHidden(True)
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))
area = Qt.LeftDockWidgetArea
self.dockAdvancedDigitizingDockWidget = addDockWidget(QgsAdvancedDigitizingDockWidget(self.dockLabeling.canvas(), self))
self.dockAdvancedDigitizingDockWidget.setVisible(False)
panel = SpectralLibraryPanel(None)
panel.setParent(self)
self.dockSpectralLibrary = addDockWidget(panel)
#except Exception as ex:
# print('Unable to create SpectralLibrary panel', file=sys.stderr)
# print(ex, file=sys.stderr)
# self.dockSpectralLibrary = None

Benjamin Jakimow
committed
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
from eotimeseriesviewer.sensorvisualization import SensorDockUI
self.dockSensors = addDockWidget(SensorDockUI(self))
self.dockCursorLocation = addDockWidget(CursorLocationInfoDock(self))
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
self.dockSystemInfo = addDockWidget(SystemInfoDock(self))
self.dockSystemInfo.setVisible(False)
self.tabifyDockWidget(self.dockCursorLocation, self.dockSensors)
self.tabifyDockWidget(self.dockCursorLocation, self.dockTaskManager)
self.tabifyDockWidget(self.dockCursorLocation, self.dockSystemInfo)
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)
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)
"""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
self.ui = TimeSeriesViewerUI()
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.showTimeSeriesDatum)
tstv = self.ui.dockTimeSeries.timeSeriesTreeView
assert isinstance(tstv, TimeSeriesTreeView)
tstv.sigMoveToDateRequest.connect(self.showTimeSeriesDatum)
self.mCurrentMapLocation = None
self.mCurrentMapSpectraLoading = 'TOP'
def initMapToolAction(action, key):
assert isinstance(action, QAction)
assert isinstance(key, str)
assert key in MapTools.mapToolKeys()
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)
tb.addAction(self.ui.dockLabeling.actionToggleEditing())
tb.addAction(self.ui.dockLabeling.actionSaveEdits())
tb.addAction(self.ui.dockLabeling.actionAddFeature())
self.ui.dockLabeling.sigMapExtentRequested.connect(self.setSpatialExtent)
self.ui.dockLabeling.sigMapCenterRequested.connect(self.setSpatialCenter)
initMapToolAction(self.ui.dockLabeling.actionAddFeature(), MapTools.AddFeature)
self.ui.dockLabeling.sigVectorLayerChanged.connect(lambda : self.spatialTemporalVis.setCurrentLayer(self.ui.dockLabeling.currentVectorSource()))
#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(self.loadExampleTimeSeries)
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)
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 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'
"""
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
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
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 = QProgressDialog()
progressDialog.setWindowTitle('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())
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 showTimeSeriesDatum(self, tsd:TimeSeriesDatum):
"""
Moves the viewport of the scroll window to a specific TimeSeriesDatum
:param tsd: TimeSeriesDatum
"""
assert isinstance(tsd, TimeSeriesDatum)
self.spatialTemporalVis.navigateToTSD(tsd)

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)
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)
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
@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, TimeSeriesDatum):
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 loadImageFiles(self, files:list):
"""
Loads image files to the time series.
:param files: [list-of-file-paths]
"""
assert isinstance(files, list)
progressDialog = QProgressDialog(parent=self.ui)
progressDialog.setWindowTitle('Load data')
progressDialog.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
progressDialog.show()
QApplication.processEvents()
self.mTimeSeries.addSources(files, progressDialog=progressDialog)
progressDialog.hide()
progressDialog.setParent(None)

Benjamin Jakimow
committed
def loadTimeSeriesDefinition(self, path:str=None, n_max:int=None, progressDialog:QProgressDialog=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):

Benjamin Jakimow
committed
b = isinstance(progressDialog, QProgressDialog)
if not b:
progressDialog = QProgressDialog(parent=self.ui)
progressDialog.setWindowTitle('Load data')
progressDialog.setWindowFlag(Qt.WindowContextHelpButtonHint, False)
progressDialog.show()
self.clearTimeSeries()
self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog)

Benjamin Jakimow
committed
if not b:
progressDialog.hide()
progressDialog.setParent(None)
"""
Create a new MapView
"""
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)

benjamin.jakimow@geo.hu-berlin.de
committed
if not re.search('timeseriesviewer', m):
if level in [Qgis.Critical, Qgis.Warning]:
self.ui.messageBar.pushMessage(tag, message, level=level)
print(r'{}({}): {}'.format(tag, level, message))

benjamin.jakimow@geo.hu-berlin.de
committed
def onTimeSeriesChanged(self, *args):

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

benjamin.jakimow@geo.hu-berlin.de
committed
if not self.mSpatialMapExtentInitialized: