Commit ec52d115 authored by Benjamin Jakimow's avatar Benjamin Jakimow

fixed MapCanvas.stretchToExtent for none-SensorProxyLayers added type hints added type hints, SensorInstrument mockup dataset copies wavelength information, optimized findMatchingSensor(), added tooltips to settings panel

pulled qps update on reading "μm"
Signed-off-by: Benjamin Jakimow's avatarbenjamin.jakimow <>
parent d945332f
......@@ -3,7 +3,7 @@
The EO Time Series Viewer is a [](QGIS) plugin to visualize time series of remote sensing images.
Its major purpose is to ease the visualization and labeling of images from multiple sensors.
Please visit [Wiki]( for more information.
Please visit for more information.
## Licence and Use ##
......@@ -1018,7 +1018,7 @@ def parseWavelength(dataset):
.format(key, len(tmp), dataset.RasterCount), file=sys.stderr)
if'wavelength.units?', key):
if'(Micrometers?|um)', values, re.I):
if'(Micrometers?|um|μm)', values, re.I):
wlu = 'um' # fix with python 3 UTF
elif'(Nanometers?|nm)', values, re.I):
wlu = 'nm'
......@@ -48,6 +48,7 @@ from eotimeseriesviewer.mapcanvas import MapCanvas
from eotimeseriesviewer.profilevisualization import SpectralTemporalVisualization
from eotimeseriesviewer.temporalprofiles import TemporalProfileLayer
from eotimeseriesviewer.mapvisualization import MapView, MapWidget
import eotimeseriesviewer.settings as eotsvSettings
from eotimeseriesviewer import SpectralProfile, SpectralLibrary, SpectralLibraryPanel
from eotimeseriesviewer.externals.qps.maptools import MapTools, CursorLocationMapTool, QgsMapToolSelect, QgsMapToolSelectionHandler
from eotimeseriesviewer.externals.qps.cursorlocationvalue import CursorLocationInfoModel, CursorLocationInfoDock
......@@ -341,9 +342,7 @@ class TimeSeriesViewer(QgisInterface, QObject):
self.mMapLayerStore = QgsMapLayerStore()
import eotimeseriesviewer.utils
eotimeseriesviewer.utils.MAP_LAYER_STORES.insert(0, self.mapLayerStore())
TimeSeriesViewer._instance = self
self.ui = TimeSeriesViewerUI()
......@@ -351,6 +350,12 @@ class TimeSeriesViewer(QgisInterface, QObject):
mvd = self.ui.dockMapViews
dts = self.ui.dockTimeSeries
mw = self.ui.mMapWidget
self.mMapLayerStore = self.mapWidget().mMapLayerStore
import eotimeseriesviewer.utils
eotimeseriesviewer.utils.MAP_LAYER_STORES.insert(0, self.mapLayerStore())
from eotimeseriesviewer.timeseries import TimeSeriesDock
from eotimeseriesviewer.mapvisualization import MapViewDock, MapWidget
assert isinstance(mvd, MapViewDock)
......@@ -374,6 +379,7 @@ class TimeSeriesViewer(QgisInterface, QObject):
self.mSpatialMapExtentInitialized = False
......@@ -1041,14 +1047,14 @@ class TimeSeriesViewer(QgisInterface, QObject):
self.mTimeSeries.loadFromFile(path, n_max=n_max, progressDialog=progressDialog, runAsync=runAsync)
def createMapView(self, name:str=None):
def createMapView(self, name:str=None)->MapView:
Creates a new MapView.
:return: MapView
return self.ui.dockMapViews.createMapView(name=name)
def mapViews(self)->list:
def mapViews(self)->typing.List[MapView]:
Returns all MapViews
:return: [list-of-MapViews]
......@@ -1079,6 +1085,13 @@ class TimeSeriesViewer(QgisInterface, QObject):
self.messageBar().pushMessage(tag, message, level)
def onSensorAdded(self, sensor:SensorInstrument):
sensorNames = eotsvSettings.value(eotsvSettings.Keys.SensorNames, default=dict())
assert isinstance(sensorNames, dict)
if in sensorNames.keys():
def onTimeSeriesChanged(self, *args):
if not self.mSpatialMapExtentInitialized:
......@@ -1209,6 +1209,9 @@ class MapCanvas(QgsMapCanvas):
if not isinstance(layer, QgsRasterLayer):
if not isinstance(spatialExtent, SpatialExtent):
spatialExtent = SpatialExtent.fromLayer(layer)
r = layer.renderer()
dp = layer.dataProvider()
newRenderer = None
......@@ -1276,7 +1279,7 @@ class MapCanvas(QgsMapCanvas):
if isinstance(layer, SensorProxyLayer):
elif isinstance(layer, QgsRasterLayer):
def saveMapImageDialog(self, fileType):
......@@ -1136,14 +1136,14 @@ class MapWidget(QFrame, loadUIFormClass(jp(DIR_UI, 'mapwidget.ui'))):
return self.mMapSize
def mapCanvases(self)->list:
def mapCanvases(self)->typing.List[MapCanvas]:
Returns all MapCanvases
:return: [list-of-MapCanvases]
return self.findChildren(MapCanvas)
def mapViewCanvases(self, mapView:MapView)->list:
def mapViewCanvases(self, mapView:MapView)->typing.List[MapCanvas]:
Returns the MapCanvases related to a MapView
:param mapView: MapView
......@@ -206,8 +206,8 @@ class SensorInstrument(QObject):
if self.mNameOriginal in [None, '']:
from eotimeseriesviewer.settings import value, Keys
sensorNames = value(Keys.SensorNames, default={})
import eotimeseriesviewer.settings as settings
sensorNames = settings.value(settings.Keys.SensorNames, default={})
sensor_name = sensorNames.get(sid, '{}bands@{}m'.format(self.nb, self.px_size_x))
......@@ -220,6 +220,11 @@ class SensorInstrument(QObject):
import uuid
path = '/vsimem/mockupImage.{}.bsq'.format(uuid.uuid4())
self.mMockupDS = TestObjects.inMemoryImage(path=path, nb=self.nb, eType=self.dataType, ns=2, nl=2)
if self.wl is not None:
self.mMockupDS.SetMetadataItem('wavelength', '{{{}}}'.format(','.join(str(wl) for wl in self.wl)))
if self.wlu is not None:
self.mMockupDS.SetMetadataItem('wavelength units', self.wlu)
s = ""
......@@ -572,6 +577,8 @@ class TimeSeriesSource(object):
uri.layerType = 'raster'
return uri
def asRasterLayer(self)->QgsRasterLayer:
return QgsRasterLayer(self.uri(),, 'gdal')
def pixelCoordinate(self, geometry)->QPoint:
......@@ -1042,10 +1049,10 @@ class SensorMatching(enum.Enum):
Describes when two different sources should be considered to be from the same sensor
DIMS = 'Image Dimensions'
DIMS_WL = 'Img. Dims. + Wavelength'
DIMS_Name = 'Img. Dims. + Name'
DIMS_WL_Name = 'Img. Dims. + Wavelength + Name'
DIMS = 'Image Dimensions only'
DIMS_WL = 'Image Dimensions + Wavelength'
DIMS_Name = 'Image Dimensions + Name'
DIMS_WL_Name = 'Image Dimensions + Wavelength + Name'
def doLoadTimeSeriesSourcesTask(qgsTask:QgsTask, dump):
......@@ -1175,27 +1182,34 @@ class TimeSeries(QAbstractItemModel):
def findMatchingSensor(self, sensorID:str)->SensorInstrument:
if isinstance(sensorID, str):
nb, px_size_x, px_size_y, dt, wl, wlu, name = sensorIDtoProperties(sensorID)
assert isinstance(sensorID, tuple) and len(sensorID) == 7
nb, px_size_x, px_size_y, dt, wl, wlu, name = sensorID
if self.mProductSimilarity == SensorMatching.DIMS:
for sensor in self.sensors():
if (sensor.nb, sensor.px_size_y, sensor.px_size_x, sensor.dataType) == (nb, px_size_y, px_size_x, dt):
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_Name:
for sensor in self.sensors():
if (sensor.nb, sensor.px_size_y, sensor.px_size_x, sensor.dataType, sensor.mNameOriginal) == (nb, px_size_y, px_size_x, dt, name):
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_WL:
for sensor in self.sensors():
if (sensor.nb, sensor.px_size_y, sensor.px_size_x, sensor.dataType, sensor.wl, sensor.wlu) == (nb, px_size_y, px_size_x, dt, wl, wlu):
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_WL_Name:
for sensor in self.sensors():
if (sensor.nb, sensor.px_size_y, sensor.px_size_x, sensor.dataType, sensor.wl, sensor.wlu, sensor.mNameOriginal) == (
nb, px_size_y, px_size_x, dt, wl, wlu, name):
return sensor
DIMS = (nb, px_size_y, px_size_x, dt)
for sensor in self.sensors():
DIMS2 = (sensor.nb, sensor.px_size_y, sensor.px_size_x, sensor.dataType)
bName = sensor.mNameOriginal == name
bWL = wlu == sensor.wlu and np.array_equal(wl, sensor.wl)
if DIMS != DIMS2:
# self.mProductSimilarity == SensorMatching.DIMS:
if self.mProductSimilarity == SensorMatching.DIMS:
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_Name and bName:
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_WL and bWL:
return sensor
elif self.mProductSimilarity == SensorMatching.DIMS_WL_Name and bName and bWL:
return sensor
return None
def sensor(self, sensorID:str)->SensorInstrument:
......@@ -6,7 +6,7 @@
......@@ -51,7 +51,7 @@
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<widget class="QWidget" name="pageGeneral">
<layout class="QVBoxLayout" name="verticalLayout_3">
......@@ -113,6 +113,9 @@
<item row="0" column="1">
<widget class="QComboBox" name="cbDateTimePrecission">
<property name="toolTip">
<string>Defines the accuracy with which source images of same sensor but different time stamps are combined into the same image group.</string>
<property name="whatsThis">
<string>Selects the precission used to extract time stamps from raster meta data.</string>
......@@ -127,6 +130,9 @@
<item row="1" column="1">
<widget class="QComboBox" name="cbSensorMatching">
<property name="toolTip">
<string>Specifies when source images should be considered to be from the same sensor/image product.</string>
<property name="whatsThis">
<string>Selects the precission used to extract time stamps from raster meta data.</string>
......@@ -219,6 +225,19 @@
<spacer name="horizontalSpacer">
<property name="orientation">
<property name="sizeHint" stdset="0">
<item row="1" column="0">
......@@ -267,6 +286,9 @@
<property name="toolTip">
<string>Intervall to check if updates are available for map canvases, e.g. to render new layers or apply renderer changes.</string>
<property name="suffix">
......@@ -303,17 +325,17 @@
......@@ -101,7 +101,7 @@ class TestInit(TestCase):
TSV.createMapView('True Color')
TSV.createMapView('Near Infrared')
while QgsApplication.taskManager().countActiveTasks() > 0 or len(TSV.timeSeries().mTasks) > 0:
Markdown is supported
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