From d1a49865cc0eeabcbfc0830b9d46a28a9543c974 Mon Sep 17 00:00:00 2001
From: Benjamin Jakimow <benjamin.jakimow@geo.hu-berlin.de>
Date: Thu, 3 Aug 2017 09:28:54 +0200
Subject: [PATCH] refactoring (a lot of) added SensorPixelDataMemoryLayer

---
 timeseriesviewer/profilevisualization.py | 293 +++++++++++++----------
 1 file changed, 166 insertions(+), 127 deletions(-)

diff --git a/timeseriesviewer/profilevisualization.py b/timeseriesviewer/profilevisualization.py
index 2845375e..c70187eb 100644
--- a/timeseriesviewer/profilevisualization.py
+++ b/timeseriesviewer/profilevisualization.py
@@ -214,9 +214,54 @@ class PlotSettingsWidgetDelegate(QStyledItemDelegate):
         else:
             raise NotImplementedError()
 
+
+class SensorPixelDataMemoryLayer(QgsVectorLayer):
+
+    def __init__(self, sensor, crs=None):
+        assert isinstance(sensor, SensorInstrument)
+        if crs is None:
+            crs = QgsCoordinateReferenceSystem('EPSG:4862')
+
+        uri = 'Point?crs={}'.format(crs.authid())
+        super(SensorPixelDataMemoryLayer, self).__init__(uri, 'Pixels_sensor_' + sensor.name(), 'memory', False)
+        self.mSensor = sensor
+
+        #initialize fields
+        assert self.startEditing()
+        # standard field names, types, etc.
+        fieldDefs = [('pxid', QVariant.String, 'integer'),
+                     ('date', QVariant.String, 'char'),
+                     ('doy', QVariant.Int, 'integer'),
+                     ('geo_x', QVariant.Double, 'decimal'),
+                     ('geo_y', QVariant.Double, 'decimal'),
+                     ('px_x', QVariant.Int, 'integer'),
+                     ('px_y', QVariant.Int, 'integer'),
+                     ]
+        # one field for each band
+        for b in range(sensor.nb):
+            fName = 'b{}'.format(b + 1)
+            fieldDefs.append((fName, QVariant.Double, 'decimal'))
+
+        # initialize fields
+        for fieldDef in fieldDefs:
+            field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
+            self.addAttribute(field)
+        self.commitChanges()
+
+    def sensor(self):
+        return self.mSensor
+
+    def nPixels(self):
+        raise NotImplementedError()
+
+    def dates(self):
+        raise NotImplementedError()
+
+
+
 class PixelCollection(QObject):
     """
-    Object to store pixel data returned by PixelLoader
+    Object to store pixel data delivered by PixelLoader
     """
 
     sigSensorAdded = pyqtSignal(SensorInstrument)
@@ -226,15 +271,22 @@ class PixelCollection(QObject):
 
 
 
-    def __init__(self, timeSeries):
-        assert isinstance(timeSeries, TimeSeries)
+    def __init__(self, ):
         super(PixelCollection, self).__init__()
 
-        self.TS = timeSeries
-        self.sensors = []
+
         self.sensorPxLayers = dict()
         self.memLyrCrs = QgsCoordinateReferenceSystem('EPSG:4326')
 
+    def connectTimeSeries(self, timeSeries):
+        self.sensorPxLayers.clear()
+
+        if isinstance(timeSeries, TimeSeries):
+            self.TS = timeSeries
+        else:
+            self.TS = None
+
+
     def getFieldDefn(self, name, values):
         if isinstance(values, np.ndarray):
             # add bands
@@ -254,7 +306,7 @@ class PixelCollection(QObject):
         assert isinstance(feature, QgsFeature)
         assert isinstance(name, str)
         i = feature.fieldNameIndex(name)
-        assert i >= 0
+        assert i >= 0, 'Field "{}" does not exist'.format(name)
         field = feature.fields()[i]
         if field.isNumeric():
             if field.type() == QVariant.Int:
@@ -265,6 +317,20 @@ class PixelCollection(QObject):
                 raise NotImplementedError()
         feature.setAttribute(i, value)
 
+    def addSensor(self, sensor):
+        assert isinstance(sensor, SensorInstrument)
+        if sensor in self.sensorPxLayers.keys():
+            return self.sensorPxLayers[sensor]
+        else:
+
+            # create new temp layer
+            #uri = 'Point?crs={}'.format(self.memLyrCrs.authid())
+            #mem = QgsVectorLayer(uri, 'Pixels_sensor_' + sensor.name(), 'memory', False)
+            mem = SensorPixelDataMemoryLayer(sensor, crs = self.memLyrCrs)
+            self.sensorPxLayers[sensor] = mem
+            self.sigSensorAdded.emit(sensor)
+            return mem
+        s = ""
 
     def addPixel(self, d):
         assert isinstance(d, PixelLoaderResult)
@@ -277,37 +343,8 @@ class PixelCollection(QObject):
             assert nb >= 1
 
             assert isinstance(tsd, TimeSeriesDatum)
-            if tsd.sensor not in self.sensorPxLayers.keys():
-                #create new temp layer
-                uri = 'Point?crs={}'.format(self.memLyrCrs.authid())
-                mem = QgsVectorLayer(uri, 'Pixels_sensor_'+tsd.sensor.name(), 'memory', False)
-
-                self.sensorPxLayers[tsd.sensor] = mem
-                assert mem.startEditing()
-
-                #standard field names, types, etc.
-                fieldDefs = [('date',QVariant.String, 'char'),
-                             ('doy', QVariant.Int, 'integer'),
-                             ('geo_x', QVariant.Double, 'decimal'),
-                             ('geo_y', QVariant.Double, 'decimal'),
-                             ('px_x', QVariant.Int, 'integer'),
-                             ('px_y', QVariant.Int, 'integer'),
-                             ]
-                for fieldDef in fieldDefs:
-                    field = QgsField(fieldDef[0], fieldDef[1], fieldDef[2])
-                    mem.addAttribute(field)
-
-                for i in range(nb):
-                    fName = 'b{}'.format(i+1)
-                    mem.addAttribute(self.getFieldDefn(fName, values))
-                assert mem.commitChanges()
-
-
-
-                self.sigSensorAdded.emit(tsd.sensor)
-                s = ""
 
-            mem = self.sensorPxLayers[tsd.sensor]
+            mem = self.addSensor(tsd.sensor)
 
 
             #insert each single pixel, line by line
@@ -364,7 +401,7 @@ class PixelCollection(QObject):
             n_deleted += n
             assert mem.commitChanges()
 
-            self.sigSensorRemoved.emit(sensor)
+            #self.sigSensorRemoved.emit(sensor)
 
         if n_deleted > 0:
             self.sigPixelRemoved.emit()
@@ -455,24 +492,29 @@ class DateTimePlotWidget(pg.PlotWidget):
 
 class PlotSettingsModel(QAbstractTableModel):
 
-    sigSensorAdded = pyqtSignal(SensorPlotSettings)
+    #sigSensorAdded = pyqtSignal(SensorPlotSettings)
     sigVisibilityChanged = pyqtSignal(SensorPlotSettings)
     sigDataChanged = pyqtSignal(SensorPlotSettings)
 
     columnames = ['sensor','nb','style','y-value']
-    def __init__(self, pxCollection, parent=None, *args):
+    def __init__(self, pixelCollection, parent=None, *args):
 
         #assert isinstance(tableView, QTableView)
 
         super(PlotSettingsModel, self).__init__(parent=parent)
+        assert isinstance(pixelCollection, PixelCollection)
 
         self.items = []
 
         self.sortColumnIndex = 0
         self.sortOrder = Qt.AscendingOrder
-        self.pxCollection = pxCollection
+        self.pxCollection = pixelCollection
+
         self.pxCollection.sigSensorAdded.connect(self.addSensor)
-        #self.pxCollection.sigSensorRemoved.connect(self.removeSensor)
+        self.pxCollection.sigSensorRemoved.connect(self.removeSensor)
+
+        for sensor in self.pxCollection.sensorPxLayers.keys():
+            self.addSensor(sensor)
 
         self.sort(0, Qt.AscendingOrder)
         s = ""
@@ -496,7 +538,6 @@ class PlotSettingsModel(QAbstractTableModel):
 
     def addSensor(self, sensor):
         assert isinstance(sensor, SensorInstrument)
-        assert sensor in self.pxCollection.sensorPxLayers.keys()
 
         sensorSettings = SensorPlotSettings(sensor, self.pxCollection.sensorPxLayers[sensor])
 
@@ -505,9 +546,6 @@ class PlotSettingsModel(QAbstractTableModel):
         self.beginInsertRows(QModelIndex(),i,i)
         self.items.append(sensorSettings)
         self.endInsertRows()
-        #self.sort(self.sortColumnIndex, self.sortOrder)
-
-        self.sigSensorAdded.emit(sensorSettings)
         sensor.sigNameChanged.connect(self.onSensorNameChanged)
 
     def removeSensor(self, sensor):
@@ -661,7 +699,6 @@ class PlotSettingsModel(QAbstractTableModel):
 
 class ProfileViewDockUI(TsvDockWidgetBase, loadUi('profileviewdock.ui')):
 
-    sigMoveToTSD = pyqtSignal(TimeSeriesDatum)
 
     def __init__(self, parent=None):
         super(ProfileViewDockUI, self).__init__(parent)
@@ -695,62 +732,14 @@ class ProfileViewDockUI(TsvDockWidgetBase, loadUi('profileviewdock.ui')):
         self.progressInfo.setText('')
         self.pxViewModel2D = None
         self.pxViewModel3D = None
-        self.pixelLoader = PixelLoader()
-        self.pixelLoader.sigPixelLoaded.connect(self.onPixelLoaded)
-        self.pixelLoader.sigLoadingStarted.connect(lambda : self.progressInfo.setText('Start loading...'))
-        self.pixelCollection = None
+
         self.tableView2DBands.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
         self.tableView2DBands.setSortingEnabled(True)
         self.btnRefresh2D.setDefaultAction(self.actionRefresh2D)
 
-    def connectTimeSeries(self, TS):
 
-        assert isinstance(TS, TimeSeries)
-        self.TS = TS
 
-        self.pixelCollection = PixelCollection(self.TS)
-        self.spectralTempVis = SpectralTemporalVisualization(self)
-        self.spectralTempVis.sigMoveToDate.connect(self.onMoveToDate)
-        self.TS.sigSensorRemoved.connect(self.spectralTempVis.removeSensor)
-        self.actionRefresh2D.triggered.connect(lambda : self.spectralTempVis.setData())
 
-    def onMoveToDate(self, date):
-        dt = np.asarray([np.abs(tsd.date - date) for tsd in self.TS])
-        i = np.argmin(dt)
-        self.sigMoveToTSD.emit(self.TS[i])
-
-
-    def onPixelLoaded(self, nDone, nMax, d):
-        self.progressBar.setValue(nDone)
-        self.progressBar.setMaximum(nMax)
-
-        assert isinstance(d, PixelLoaderResult)
-
-
-        QgsApplication.processEvents()
-        bn = os.path.basename(d.source)
-        if d.success():
-
-            t = 'Last loaded from {}.'.format(bn)
-            if self.pixelCollection is not None:
-                self.pixelCollection.addPixel(d)
-        else:
-            t = 'Failed loading from {}.'.format(bn)
-        self.progressInfo.setText(t)
-
-
-
-    def loadCoordinate(self, spatialPoint):
-
-        assert isinstance(spatialPoint, SpatialPoint)
-        from timeseriesviewer.timeseries import TimeSeries
-        assert isinstance(self.TS, TimeSeries)
-
-        files = [tsd.pathImg for tsd in self.TS if tsd.isVisible()]
-        self.pixelLoader.setNumberOfProcesses(SETTINGS.value('n_threads', 1))
-        self.pixelLoader.startLoading(files, spatialPoint)
-        if self.spectralTempVis is not None:
-            self.setWindowTitle('{} | {} {}'.format(self.baseTitle, str(spatialPoint.toString()), spatialPoint.crs().authid()))
 
 def date2num(d):
     d2 = d.astype(datetime.datetime)
@@ -783,6 +772,9 @@ class SpectralTemporalVisualization(QObject):
         assert isinstance(ui, ProfileViewDockUI)
         self.ui = ui
 
+        self.pixelLoader = PixelLoader()
+        self.pixelLoader.sigPixelLoaded.connect(self.onPixelLoaded)
+        self.pixelLoader.sigLoadingStarted.connect(lambda: self.ui.progressInfo.setText('Start loading...'))
 
 
         self.plot_initialized = False
@@ -792,39 +784,76 @@ class SpectralTemporalVisualization(QObject):
         self.plot2D.plotItem.getViewBox().sigMoveToDate.connect(self.sigMoveToDate)
 
         self.plot3D = ui.plotWidget3D
-        self.pxCollection = ui.pixelCollection
+        self.pxCollection = PixelCollection()
+        self.pxCollection.sigPixelAdded.connect(self.requestUpdate)
+        self.pxCollection.sigPixelRemoved.connect(self.clear)
+
+        self.plotSettingsModel = None
+
+        self.pixelLoader.sigLoadingStarted.connect(self.clear)
+        self.pixelLoader.sigLoadingFinished.connect(lambda : self.plot2D.enableAutoRange('x', False))
+        self.ui.actionRefresh2D.triggered.connect(lambda: self.setData())
+
+        # self.VIEW.setItemDelegateForColumn(3, PointStyleDelegate(self.VIEW))
+        self.plotData2D = dict()
+        self.plotData3D = dict()
+
+        self.updateRequested = True
+        self.updateTimer = QTimer(self)
+        self.updateTimer.timeout.connect(self.updatePlot)
+        self.updateTimer.start(2000)
+
+        self.sigMoveToDate.connect(self.onMoveToDate)
+
+    def connectTimeSeries(self, TS):
+
+        assert isinstance(TS, TimeSeries)
+        self.TS = TS
+
+        self.pxCollection.connectTimeSeries(self.TS)
+
+        self.TS.sigSensorRemoved.connect(self.removeSensor)
 
         self.plotSettingsModel = PlotSettingsModel(self.pxCollection, parent=self)
-        self.plotSettingsModel.sigSensorAdded.connect(self.requestUpdate)
+
         self.plotSettingsModel.sigVisibilityChanged.connect(self.setVisibility)
-        #self.plotSettingsModel.sigVisibilityChanged.connect(self.loadData)
+        # self.plotSettingsModel.sigVisibilityChanged.connect(self.loadData)
         self.plotSettingsModel.sigDataChanged.connect(self.requestUpdate)
 
-        #self.plotSettingsModel.sigVisiblityChanged.connect(self.refresh)
+        # self.plotSettingsModel.sigVisiblityChanged.connect(self.refresh)
         self.plotSettingsModel.rowsInserted.connect(self.onRowsInserted)
-        #self.plotSettingsModel.modelReset.connect(self.updatePersistantWidgets)
+        # self.plotSettingsModel.modelReset.connect(self.updatePersistantWidgets)
         self.TV.setModel(self.plotSettingsModel)
         self.delegate = PlotSettingsWidgetDelegate(self.TV)
         self.TV.setItemDelegateForColumn(2, self.delegate)
         self.TV.setItemDelegateForColumn(3, self.delegate)
-        #self.TV.setItemDelegateForColumn(3, PointStyleDelegate(self.TV))
+        # self.TV.setItemDelegateForColumn(3, PointStyleDelegate(self.TV))
 
-        for s in self.pxCollection.sensorPxLayers.keys():
-            self.plotSettingsModel.addSensor(s)
 
-        self.pxCollection.sigPixelAdded.connect(self.requestUpdate)
-        self.pxCollection.sigPixelRemoved.connect(self.clear)
-        self.ui.pixelLoader.sigLoadingStarted.connect(self.clear)
-        self.ui.pixelLoader.sigLoadingFinished.connect(lambda : self.plot2D.enableAutoRange('x', False))
+    sigMoveToTSD = pyqtSignal(TimeSeriesDatum)
 
-        # self.VIEW.setItemDelegateForColumn(3, PointStyleDelegate(self.VIEW))
-        self.plotData2D = dict()
-        self.plotData3D = dict()
+    def onMoveToDate(self, date):
+        dt = np.asarray([np.abs(tsd.date - date) for tsd in self.TS])
+        i = np.argmin(dt)
+        self.sigMoveToTSD.emit(self.TS[i])
 
-        self.updateRequested = True
-        self.updateTimer = QTimer(self)
-        self.updateTimer.timeout.connect(self.updatePlot)
-        self.updateTimer.start(2000)
+
+    def onPixelLoaded(self, nDone, nMax, d):
+        self.ui.progressBar.setValue(nDone)
+        self.ui.progressBar.setMaximum(nMax)
+
+        assert isinstance(d, PixelLoaderResult)
+
+
+        QgsApplication.processEvents()
+        bn = os.path.basename(d.source)
+        if d.success():
+
+            t = 'Last loaded from {}.'.format(bn)
+            self.pxCollection.addPixel(d)
+        else:
+            t = 'Failed loading from {}.'.format(bn)
+        self.ui.progressInfo.setText(t)
 
     def requestUpdate(self, *args):
         self.updateRequested = True
@@ -879,8 +908,8 @@ class SpectralTemporalVisualization(QObject):
             p.clear()
             p.update()
 
-        if len(self.ui.TS) > 0:
-            rng = [self.ui.TS[0].date, self.ui.TS[-1].date]
+        if len(self.TS) > 0:
+            rng = [self.TS[0].date, self.TS[-1].date]
             rng = [date2num(d) for d in rng]
             self.plot2D.getPlotItem().setRange(xRange=rng)
         QApplication.processEvents()
@@ -888,6 +917,19 @@ class SpectralTemporalVisualization(QObject):
             pass
 
 
+    def loadCoordinate(self, spatialPoint):
+
+        assert isinstance(spatialPoint, SpatialPoint)
+        assert isinstance(self.TS, TimeSeries)
+
+        files = [tsd.pathImg for tsd in self.TS if tsd.isVisible()]
+
+        self.pixelLoader.setNumberOfProcesses(SETTINGS.value('n_threads', 1))
+        self.pixelLoader.startLoading(files, spatialPoint)
+
+        self.ui.setWindowTitle('{} | {} {}'.format(self.ui.baseTitle, str(spatialPoint.toString()), spatialPoint.crs().authid()))
+
+
     def setVisibility(self, sensorView):
         assert isinstance(sensorView, SensorPlotSettings)
         self.setVisibility2D(sensorView)
@@ -1073,22 +1115,19 @@ if __name__ == '__main__':
     from timeseriesviewer import sandbox
     qgsApp = sandbox.initQgisEnvironment()
 
-    d = ProfileViewDockUI()
-    d.show()
+    ui = ProfileViewDockUI()
+    ui.show()
 
     if True:
-        from timeseriesviewer.tests import *
-
-        #TS = TestObjects.timeSeries()
-        #d.connectTimeSeries(TS)
         TS = TimeSeries()
-        d.connectTimeSeries(TS)
-        print('Load TS...')
-        TS.loadFromFile(r'O:\SenseCarbonProcessing\BJ_Multitemp2017\timeseriesCBERS_LS_RE.csv')
-        print('Loading done')
+        SViz = SpectralTemporalVisualization(ui)
+        SViz.connectTimeSeries(TS)
+
+        from example.Images import Img_2014_01_15_LC82270652014015LGN00_BOA
+        TS.addFiles([Img_2014_01_15_LC82270652014015LGN00_BOA])
         ext = TS.getMaxSpatialExtent()
         cp = SpatialPoint(ext.crs(),ext.center())
-        d.loadCoordinate(cp)
+        SViz.loadCoordinate(cp)
 
     qgsApp.exec_()
     qgsApp.exitQgis()
-- 
GitLab