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 ]

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
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
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.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.sensorvisualization import SensorDockUI
self.dockSensors = addDockWidget(SensorDockUI(self))
from eotimeseriesviewer.mapvisualization import MapViewDock
self.dockMapViews = addDockWidget(MapViewDock(self))
self.dockCursorLocation = addDockWidget(CursorLocationInfoDock(self))
# self.tabifyDockWidget(self.dockMapViews, self.dockRendering)
self.tabifyDockWidget(self.dockSensors, self.dockCursorLocation)

benjamin.jakimow@geo.hu-berlin.de
committed
area = Qt.BottomDockWidgetArea
# from timeseriesviewer.mapvisualization import MapViewDockUI
# self.dockMapViews = addDockWidget(MapViewDockUI(self))
self.dockTimeSeries = addDockWidget(TimeSeriesDockUI(self))
from eotimeseriesviewer.profilevisualization import ProfileViewDockUI
self.dockProfiles = addDockWidget(ProfileViewDockUI(self))
from eotimeseriesviewer.labeling import LabelingDock
self.dockLabeling = addDockWidget(LabelingDock(self))
#try:
panel = SpectralLibraryPanel(None)
panel.setParent(self)
self.dockSpectralLibrary = addDockWidget(panel)
self.tabifyDockWidget(self.dockTimeSeries, self.dockSpectralLibrary)
#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.dockProfiles)

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

benjamin.jakimow@geo.hu-berlin.de
committed
area = Qt.RightDockWidgetArea
from eotimeseriesviewer.systeminfo import SystemInfoDock

benjamin.jakimow@geo.hu-berlin.de
committed
self.dockSystemInfo = addDockWidget(SystemInfoDock(self))
self.dockSystemInfo.setVisible(False)
for dock in self.findChildren(QDockWidget):
if len(dock.actions()) > 0:
s = ""
self.menuPanels.addAction(dock.toggleViewAction())
self.mMapToolActions = [self.actionZoomPixelScale,
self.actionZoomFullExtent,
self.actionZoomIn,
self.actionZoomOut,
self.actionPan,
self.actionIdentify]
self.dockTimeSeries.raise_()
for a in self.mMapToolActions:
assert isinstance(a, QAction)
a.toggled.connect(self.onActionToggled)
def onActionToggled(self, b:bool):
action = QApplication.instance().sender()
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 _blockSignals(self, widgets, block=True):
states = dict()
if isinstance(widgets, dict):
for w, block in widgets.items():
states[w] = w.blockSignals(block)
else:
for w in widgets:
states[w] = w.blockSignals(block)
return states
sigSubsetSizeChanged = pyqtSignal(QSize)
def setSubsetSize(self, size, blockSignal=False):
old = self.subsetSize()
w = [self.spinBoxSubsetSizeX, self.spinBoxSubsetSizeY]
if blockSignal:
states = self._blockSignals(w, True)
self.spinBoxSubsetSizeX.setValue(size.width())
self.spinBoxSubsetSizeY.setValue(size.height())
self._setUpdateBehaviour()
if blockSignal:
self._blockSignals(states)
elif old != size:
self.sigSubsetSizeChanged(size)
def setProgress(self, value, valueMax=None, valueMin=0):
p = self.progressBar
if valueMin is not None and valueMin != self.progessBar.minimum():
p.setMinimum(valueMin)
if valueMax is not None and valueMax != self.progessBar.maximum():
p.setMaximum(valueMax)
self.progressBar.setValue(value)
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
"""
# assert TimeSeriesViewer.instance() is None
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()
import qgis.utils
iface = qgis.utils.iface
assert isinstance(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)
# init other GUI components
# self.ICP = D.scrollAreaSubsetContent.layout()
# D.scrollAreaMapViews.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)
# self.BVP = self.ui.scrollAreaMapViews.layout()
# D.dockNavigation.connectTimeSeries(self.TS)
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.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
# self.spatialTemporalVis.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
# self.spatialTemporalVis.sigShowProfiles.connect(self.spectralTemporalVis.loadCoordinate)

Benjamin Jakimow
committed
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.setBackgroundColor)
self.spatialTemporalVis.sigCRSChanged.connect(self.ui.dockMapViews.setCrs)
self.spatialTemporalVis.sigMapSizeChanged.connect(self.ui.dockMapViews.setMapSize)
self.spectralTemporalVis.sigMoveToTSD.connect(self.showTimeSeriesDatum)
self.spectralTemporalVis.ui.actionLoadProfileRequest.triggered.connect(self.ui.actionIdentifyTemporalProfile.trigger)
tstv = self.ui.dockTimeSeries.tableView_TimeSeries
assert isinstance(tstv, TimeSeriesTableView)
tstv.sigMoveToDateRequest.connect(self.showTimeSeriesDatum)
# init map tools
self.mMapTools = []
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)
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)
#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'))
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)

benjamin.jakimow@geo.hu-berlin.de
committed
from eotimeseriesviewer.widgets import AboutDialogUI
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))
assert isinstance(self.ui.dockSpectralLibrary.SLW, SpectralLibraryWidget)
self.ui.dockSpectralLibrary.SLW.sigLoadFromMapRequest.connect(self.ui.actionIdentifySpectralProfile.trigger)
self.ui.dockSpectralLibrary.SLW.setMapInteraction(True)
self.ui.dockSpectralLibrary.SLW.setCurrentProfilesMode(SpectralLibraryWidget.CurrentProfilesMode.automatically)
# 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()
def onMapViewAdded(self, mapView):
mapView.addLayer(self.spectralTemporalVis.temporalProfileLayer())
mapView.addLayer(self.spectralLibrary())
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)
#todo: move TableViews to as well

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
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:
"""
self.ui.actionImportExtent.triggered.connect(lambda: self.spatialTemporalVis.setSpatialExtent(SpatialExtent.fromMapCanvas(self.iface.mapCanvas())))
self.ui.actionExportExtent.triggered.connect(lambda: self.iface.mapCanvas().setExtent(self.spatialTemporalVis.spatialExtent().toCrs(self.iface.mapCanvas().mapSettings().destinationCrs())))
self.ui.actionExportCenter.triggered.connect(lambda: self.iface.mapCanvas().setCenter(self.spatialTemporalVis.spatialExtent().spatialCenter()))
self.ui.actionImportCenter.triggered.connect(lambda: self.spatialTemporalVis.setSpatialCenter(SpatialPoint.fromMapCanvasCenter(self.iface.mapCanvas())))
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:
setValue(key, defaults[key])
self.mTimeSeries.setDateTimePrecision(value(Keys.DateTimePrecision))
self.spatialTemporalVis.mMapRefreshTimer.start(value(Keys.MapUpdateInterval))
self.spatialTemporalVis.setBackgroundColor(value(Keys.MapBackgroundColor))
self.spatialTemporalVis.setMapSize(value(Keys.MapSize))
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
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
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
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:
"""
# disconnect previous map-tools?
del self.mMapTools[:]
for canvas in self.mapCanvases():
self.setSingleMapTool(canvas, mapToolKey, *args, **kwds)
def currentMapToolKey(self)->str:
"""
Returns the MapToolKey that identifies a MapTool
:return: str
"""
if self.ui.actionPan.isChecked():
return MapTools.Pan
if self.ui.actionZoomIn.isChecked():
return MapTools.ZoomIn
if self.ui.actionZoomOut.isChecked():
return MapTools.ZoomOut
if self.ui.actionZoomPixelScale.isChecked():
return MapTools.ZoomPixelScale
if self.ui.actionZoomFullExtent.isChecked():
return MapTools.ZoomFull
if self.ui.actionIdentify.isChecked():
return MapTools.CursorLocation
else:
# default key
return MapTools.CursorLocation
def setSingleMapTool(self, canvas:QgsMapCanvas, mapToolKey, *args, **kwds):
"""
Sets the QgsMapTool for a single canvas
:param canvas: QgsMapCanvas
:param mapToolKey:
:param args:
:param kwds:
:return:
"""
mt = None
if mapToolKey in MapTools.mapToolKeys():
mt = MapTools.create(mapToolKey, canvas, *args, **kwds)
if isinstance(mapToolKey, QgsMapTool):
mt = MapTools.copy(mapToolKey, canvas, *args, **kwds)
if isinstance(mt, QgsMapTool):
canvas.setMapTool(mt)
self.mMapTools.append(mt)
# if required, link map-tool with specific EnMAP-Box slots
if isinstance(mt, CursorLocationMapTool):
mt.sigLocationRequest[SpatialPoint, QgsMapCanvas].connect(self.setCurrentLocation)
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().crs()))
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)
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
@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 = self.spatialTemporalVis.DVC.tsdFromMapCanvas(mapCanvas)
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)
self.mTimeSeries.addSources(files)
def loadTimeSeriesDefinition(self, path=None, n_max=None):
"""
Loads a time series definition file
:param path:
:param n_max:
:return:
"""
defFile = s.value('file_ts_definition')
defDir = None
if defFile is not None:
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):
M = self.ui.dockTimeSeries.tableView_TimeSeries.model()
M.beginResetModel()
self.clearTimeSeries()
self.mTimeSeries.loadFromFile(path, n_max=n_max)
M.endResetModel()
"""
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:
if len(self.mTimeSeries) > 0:
if len(self.spatialTemporalVis.MVC) == 0:

Benjamin Jakimow
committed
# add an empty MapView by default

Benjamin Jakimow
committed
#self.spatialTemporalVis.createMapView()
extent = self.mTimeSeries.maxSpatialExtent()

benjamin.jakimow@geo.hu-berlin.de
committed
self.spatialTemporalVis.setCrs(extent.crs())
self.spatialTemporalVis.setSpatialExtent(extent)

benjamin.jakimow@geo.hu-berlin.de
committed
self.mSpatialMapExtentInitialized = True

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

benjamin.jakimow@geo.hu-berlin.de
committed
if len(self.mTimeSeries) == 0:

benjamin.jakimow@geo.hu-berlin.de
committed
self.mSpatialMapExtentInitialized = False
def saveTimeSeriesDefinition(self):
defFile = s.value('FILE_TS_DEFINITION')
if defFile is not None:
defFile = os.path.dirname(defFile)
filters = "CSV (*.csv *.txt);;" + \
"All files (*.*)"
path, filter = QFileDialog.getSaveFileName(caption='Save Time Series definition', filter=filters, directory=defFile)
path = self.mTimeSeries.saveToFile(path)
if path is not None:
s.setValue('FILE_TS_DEFINITION', path)
def loadTimeSeriesStack(self):
from eotimeseriesviewer.stackedbandinput import StackedBandInputDialog
d = StackedBandInputDialog(parent=self.ui)
if d.exec_() == QDialog.Accepted:
self.addTimeSeriesImages(writtenFiles)

Benjamin Jakimow
committed
"""
Loads an example time series
:param n: int, max. number of images to load. Useful for developer test-cases
"""
import example.Images
files = list(file_search(os.path.dirname(example.Images.__file__), '*.tif'))

Benjamin Jakimow
committed
if isinstance(n, bool) or not isinstance(n, int):
n = len(files)

Benjamin Jakimow
committed

Benjamin Jakimow
committed
n = min(n, len(files))
n = max(1, n)
self.addTimeSeriesImages(files[0:n])

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

benjamin.jakimow@geo.hu-berlin.de
committed
def timeSeries(self)->TimeSeries:
"""
Returns the TimeSeries instance.
:return: TimeSeries
"""
return self.mTimeSeries
# noinspection PyMethodMayBeStatic
def tr(self, message):
"""Get the translation for a string using Qt translation API.
We implement this ourselves since we do not inherit QObject.
:param message: String for translation.
:type message: str, QString
:returns: Translated version of message.
:rtype: QString
"""
# noinspection PyTypeChecker,PyArgumentList,PyCallByClass

benjamin.jakimow@geo.hu-berlin.de
committed
return QCoreApplication.translate('HUBTSV', message)
"""Removes the plugin menu item and icon """
self.iface.removeToolBarIcon(self.action)

benjamin.jakimow@geo.hu-berlin.de
committed
#QApplication.processEvents()
self.ui.show()
def clearLayoutWidgets(self, L):
if L is not None:
while L.count():
w = L.takeAt(0)
if w.widget():
w.widget().deleteLater()

Benjamin Jakimow
committed
QApplication.processEvents()

Benjamin Jakimow
committed
def addVectorData(self, files=None):
defDir = s.value('DIR_FILESEARCH')

Benjamin Jakimow
committed
filters = QgsProviderRegistry.instance().fileVectorFilters()
files, filter = QFileDialog.getOpenFileNames(directory=defDir, filter=filters)

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

Benjamin Jakimow
committed
if len(files) > 0 and os.path.exists(files[0]):
dn = os.path.dirname(files[0])
s.setValue('DIR_FILESEARCH', dn)
if files:
vectorLayers = []
for f in files:
try:
l = QgsVectorLayer(f, os.path.basename(f))
vectorLayers.append(l)
except Exception as ex:
pass
#QgsProject.instance().addMapLayers(vectorLayers)
self.mapLayerStore().addMapLayers(vectorLayers)

Benjamin Jakimow
committed
def addTimeSeriesImages(self, files:list):
"""
Adds images to the time series
:param files:
"""

Benjamin Jakimow
committed
if files is None:
s = settings()
filters = QgsProviderRegistry.instance().fileRasterFilters()
files, filter = QFileDialog.getOpenFileNames(directory=defDir, filter=filters)
if len(files) > 0 and os.path.exists(files[0]):
dn = os.path.dirname(files[0])
self.mTimeSeries.addSources(files)
M = self.ui.dockTimeSeries.tableView_TimeSeries.model()
self.mTimeSeries.clear()
def getSelectedTSDs(self):
TV = self.ui.tableView_TimeSeries
TVM = TV.model()
return [TVM.getTimeSeriesDatumFromIndex(idx) for idx in TV.selectionModel().selectedRows()]
def disconnect_signal(signal):
while True:
try:
signal.disconnect()
except TypeError:
break