Commit 5b10091b authored by Benjamin Jakimow's avatar Benjamin Jakimow
Browse files

TimeSeriesTreeView:


 - renamed sigMoveToDateRequest to sigMoveToDate
 - added sigMoveToExtent
 - modified context menu

fixed example data loading

MapCanvas:
- setTSD now sets the map canvas QGIS temporal range as well (mit be used by layer styles)

mapvisualization.py: refactoring
MapWidget:
- shows slider date
- added .sliderDate(...)

temporalprofiles.py:
- refactoring
- fixed TemporalProfile.loadMissingData
- fixed TemporalProfileLoaderTask

Signed-off-by: Benjamin Jakimow's avatarBenjamin Jakimow <benjamin.jakimow@geo.hu-berlin.de>
parent 34d48eb9
Pipeline #12391 failed
......@@ -525,8 +525,9 @@ class EOTimeSeriesViewer(QgisInterface, QObject):
tstv = self.ui.dockTimeSeries.timeSeriesTreeView
assert isinstance(tstv, TimeSeriesTreeView)
tstv.sigMoveToDateRequest.connect(self.setCurrentDate)
tstv.sigMoveToDate.connect(self.setCurrentDate)
tstv.sigMoveToSource.connect(self.setCurrentSource)
tstv.sigMoveToExtent.connect(self.setSpatialExtent)
tstv.sigSetMapCrs.connect(self.setCrs)
self.mCurrentMapLocation = None
self.mCurrentMapSpectraLoading = 'TOP'
......@@ -1386,7 +1387,7 @@ class EOTimeSeriesViewer(QgisInterface, QObject):
"""
import example.Images
exampleDataDir = os.path.dirname(example.__file__)
exampleDataDir = os.path.dirname(example.Images.__file__)
rasterFiles = list(file_search(exampleDataDir, '*.tif', recursive=True))
vectorFiles = list(file_search(exampleDataDir, re.compile(r'.*\.(gpkg|shp)$'), recursive=True))
if isinstance(n, bool) or not isinstance(n, int):
......
......@@ -519,7 +519,7 @@ class MapCanvas(QgsMapCanvas):
self.addToRefreshPipeLine(mapView.mapBackgroundColor())
self.addToRefreshPipeLine(MapCanvas.Command.UpdateMapItems)
def setTSD(self, tsd:TimeSeriesDate):
def setTSD(self, tsd: TimeSeriesDate):
"""
Sets the TimeSeriesDate this map-canvas is linked to
:param tsd:
......@@ -533,8 +533,8 @@ class MapCanvas(QgsMapCanvas):
self.mTSD.sensor().sigNameChanged.disconnect(self.updateScope)
self.mTSD = tsd
if isinstance(tsd, TimeSeriesDate):
self.setTemporalRange(tsd.temporalRange())
self.mTSD.sensor().sigNameChanged.connect(self.updateScope)
self.updateScope()
......
This diff is collapsed.
......@@ -27,8 +27,20 @@ import datetime
import re
from collections import OrderedDict
from qgis.gui import *
from qgis.core import *
from qgis.core import QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsMessageOutput, QgsCoordinateReferenceSystem, \
Qgis, QgsWkbTypes, QgsTask, QgsProviderRegistry, QgsMapLayerStore, QgsFeature, QgsDateTimeRange, \
QgsTextFormat, QgsProject, QgsSingleSymbolRenderer, QgsGeometry, QgsApplication, QgsFillSymbol, \
QgsTask, QgsRasterBandStats, QgsRectangle, QgsRasterDataProvider, QgsTaskManager, QgsPoint, QgsPointXY, \
QgsRasterLayerTemporalProperties, QgsMimeDataUtils, QgsCoordinateTransform, QgsFeatureRequest, \
QgsVectorLayerCache, QgsVectorFileWriter, \
QgsConditionalStyle, QgsConditionalLayerStyles, \
QgsField, QgsFields, QgsExpressionContext, QgsExpression
from qgis.gui import *
from qgis.gui import QgsMapCanvas, QgsStatusBar, QgsFileWidget, \
QgsMessageBar, QgsMessageViewer, QgsDockWidget, QgsTaskManagerWidget, QgisInterface, \
QgsAttributeTableFilterModel, QgsIFeatureSelectionManager, QgsAttributeTableModel, QgsAttributeTableView
from qgis.analysis import *
from qgis.PyQt.QtCore import *
from qgis.PyQt.QtGui import *
......@@ -237,19 +249,18 @@ def num2date(n, dt64=True, qDate=False):
return date
#return np.datetime64('{:04}-01-01'.format(year), 'D') + np.timedelta64(int(yearElapsed), 'D')
def bandIndex2bandKey(i : int):
assert i >= 0
return 'b{}'.format(i + 1)
def bandKey2bandIndex(key: str):
match = rxBandKeyExact.search(key)
assert match
idx = int(match.group()[1:]) - 1
return idx
class TemporalProfile(QObject):
sigLoadMissingImageDataRequest = pyqtSignal(list)
......@@ -366,8 +377,10 @@ class TemporalProfile(QObject):
"""
Loads the missing data for this profile (synchronous execution, may take some time).
"""
qgsTask = TemporalProfileLoaderTask(self.mLayer, required_profiles=[self], callback=self.mLayer.updateProfileData)
qgsTask.finished(qgsTask.run(), self)
qgsTask = TemporalProfileLoaderTask(self.mLayer,
required_profiles=[self],
callback=self.mLayer.updateProfileData)
qgsTask.finished(qgsTask.run())
def missingBandIndices(self, tsd: TimeSeriesDate, required_indices: typing.List[int] = None):
"""
......@@ -389,6 +402,7 @@ class TemporalProfile(QObject):
return [i for i in required_indices if i not in existingBandIndices]
def plot(self):
from .profilevisualization import TemporalProfilePlotStyle, TemporalProfilePlotDataItem
for sensor in self.mTimeSeries.sensors():
assert isinstance(sensor, SensorInstrument)
......@@ -775,7 +789,6 @@ class TemporalProfileLayer(QgsVectorLayer):
# geometryChanged (QgsFeatureId fid, const QgsGeometry &geometry)
s = ""
def onFeaturesAdded(self, layerID, addedFeatures):
"""
Create a TemporalProfile object for each QgsFeature added to the backend QgsVectorLayer
......@@ -826,7 +839,7 @@ class TemporalProfileLayer(QgsVectorLayer):
#styles.setRowStyles([red])
def createTemporalProfiles(self,
coordinates:typing.List[SpatialPoint],
coordinates: typing.List[SpatialPoint],
names:typing.List[str] = None) -> typing.List[TemporalProfile]:
"""
Creates temporal profiles for a list of coordinates
......@@ -1069,7 +1082,7 @@ class TemporalProfileLoaderTask(QgsTask):
self.GEOMETRY_CACHE[tp.id()] = dict()
missingTPData = dict()
for sensor, tsds in required_tsds.items():
for tsd in timeSeries:
for tsd in timeSeries.tsds(sensor=sensor):
existingTSDData: dict = tp.data(tsd)
missingTSDData: dict = dict()
for band_key in self.mRequiredSensorBandKeys[sensor]:
......@@ -1164,6 +1177,8 @@ class TemporalProfileLoaderTask(QgsTask):
band: gdal.Band = ds.GetRasterBand(bandKey2bandIndex(band_key) + 1)
if not isinstance(band, gdal.Band):
s = ""
continue
for tpID, px_idx in PX_INDICES.items():
px_x, px_y = px_idx
xoff = int(min(px_x))
......
......@@ -42,6 +42,16 @@ from qgis.PyQt.QtXml import QDomDocument
from qgis import *
from qgis.core import *
from qgis.core import QgsMapLayer, QgsRasterLayer, QgsVectorLayer, QgsMessageOutput, QgsCoordinateReferenceSystem, \
Qgis, QgsWkbTypes, QgsTask, QgsProviderRegistry, QgsMapLayerStore, QgsFeature, QgsDateTimeRange, \
QgsTextFormat, QgsProject, QgsSingleSymbolRenderer, QgsGeometry, QgsApplication, QgsFillSymbol, \
QgsTask, QgsRasterBandStats, QgsRectangle, QgsRasterDataProvider, QgsTaskManager, QgsPoint, QgsPointXY, \
QgsRasterLayerTemporalProperties, QgsMimeDataUtils, QgsCoordinateTransform
from qgis.gui import *
from qgis.gui import QgsMapCanvas, QgsStatusBar, QgsFileWidget, \
QgsMessageBar, QgsMessageViewer, QgsDockWidget, QgsTaskManagerWidget, QgisInterface
from qgis.gui import *
from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *
......@@ -852,6 +862,17 @@ class TimeSeriesDate(QAbstractTableModel):
"""
return [s.qgsMimeDataUtilsUri() for s in self.sources()]
def temporalRange(self) -> QgsDateTimeRange:
d1 = d2 = self.mDate.astype(object)
if len(self.mSources) > 0:
dates = [s.date() for s in self]
d1 = min(dates).astype(object)
d2 = max(dates).astype(object)
return QgsDateTimeRange(QDateTime(d1), QDateTime(d2))
def date(self) -> np.datetime64:
"""
Returns the observation date
......@@ -2215,8 +2236,9 @@ class TimeSeries(QAbstractItemModel):
class TimeSeriesTreeView(QTreeView):
sigMoveToDateRequest = pyqtSignal(TimeSeriesDate)
sigMoveToDate = pyqtSignal(TimeSeriesDate)
sigMoveToSource = pyqtSignal(TimeSeriesSource)
sigMoveToExtent = pyqtSignal(SpatialExtent)
sigSetMapCrs = pyqtSignal(QgsCoordinateReferenceSystem)
def __init__(self, parent=None):
......@@ -2255,20 +2277,29 @@ class TimeSeriesTreeView(QTreeView):
menu.addSeparator()
if isinstance(node, TimeSeriesDate):
a = menu.addAction('Show map for {}'.format(node.date()))
a.setToolTip('Shows the map related to this time series date.')
a.triggered.connect(lambda *args, tsd=node: self.sigMoveToDateRequest.emit(tsd))
a = menu.addAction('Move to date {}'.format(node.date()))
a.setToolTip(f'Sets the current map date to {node.date()}.')
a.triggered.connect(lambda *args, tsd=node: self.sigMoveToDate.emit(tsd))
a = menu.addAction('Move to extent'.format(node.date()))
a.setToolTip(f'Sets the current map extent')
a.triggered.connect(lambda *args, tsd=node: self.onMoveToExtent(tsd.spatialExtent()))
menu.addSeparator()
elif isinstance(node, TimeSeriesSource):
a = menu.addAction('Show {}'.format(node.name()))
a.setToolTip('Shows the map with {} and zooms to'.format(node.uri()))
a.setToolTip(f'Sets the current map date to {node.date()} and zooms\nto the spatial extent of {node.uri()}')
a.triggered.connect(lambda *args, tss=node: self.sigMoveToSource.emit(tss))
menu.addSeparator()
a = menu.addAction('Set Crs to maps')
a.setToolTip(f'Sets the map projection to that of this image ({node.crs().description()})')
a = menu.addAction(f'Set map CRS from {node.name()}')
a.setToolTip(f'Sets the map projection to {node.crs().description()}')
a.triggered.connect(lambda *args, crs=node.crs(): self.sigSetMapCrs.emit(crs))
menu.addSeparator()
a = menu.addAction('Set date(s) invisible')
a.setToolTip('Hides the selected time series dates from being shown in a map.')
a.triggered.connect(lambda *args, tsds=selectedTSDs: self.timeseries().showTSDs(tsds, False))
......@@ -2284,6 +2315,10 @@ class TimeSeriesTreeView(QTreeView):
menu.popup(QCursor.pos())
def onMoveToExtent(self, extent:SpatialExtent):
if isinstance(extent, SpatialExtent):
self.sigMoveToExtent.emit(extent)
def openInQGIS(self, tssList: typing.List[TimeSeriesSource]):
import qgis.utils
......
......@@ -74,7 +74,7 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,0,0,0">
<item>
<widget class="QToolButton" name="btnFirst">
<property name="text">
......@@ -117,13 +117,20 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="tbSliderDate">
<property name="text">
<string>0000-00-00</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="mTimeSlider">
<property name="singleStep">
<number>1</number>
</property>
<property name="tracking">
<bool>false</bool>
<bool>true</bool>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
......
......@@ -128,7 +128,8 @@ class TestTemporalProfiles(EOTSVTestCase):
self.assertIsInstance(tp, TemporalProfile)
tp.loadMissingData()
temporalProfiles = [tp]
temporalProfiles.extend(lyr.createTemporalProfiles((SpatialPoint(center.crs(), center.x() - 50, center.y() + 50))))
temporalProfiles.extend(lyr.createTemporalProfiles((
SpatialPoint(center.crs(), center.x() - 50, center.y() + 50))))
for tp in temporalProfiles:
tp.loadMissingData()
......@@ -260,7 +261,6 @@ class TestTemporalProfiles(EOTSVTestCase):
self.assertEqual(len(tps), n)
model = TemporalProfileTableModel(layer)
self.assertEqual(model.rowCount(), n)
pd = ProfileViewDock()
......@@ -275,9 +275,6 @@ class TestTemporalProfiles(EOTSVTestCase):
pd.loadCoordinate(point3)
pd.loadCoordinate(point2)
#from eotimeseriesviewer.externals.qps.resources import ResourceBrowser
#browser = ResourceBrowser()
self.showGui([pd])
......
......@@ -353,7 +353,7 @@ class TestTimeSeries(EOTSVTestCase):
def test_timeseries(self):
files = list(file_search(os.path.dirname(example.__file__), '*.tif', recursive=True))
files = list(file_search(os.path.dirname(example.Images.__file__), '*.tif', recursive=True))
addedDates = []
removedDates = []
......@@ -534,4 +534,4 @@ class TestTimeSeries(EOTSVTestCase):
if __name__ == '__main__':
unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports'), buffer=False)
exit(0)
\ No newline at end of file
exit(0)
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment