Skip to content
Snippets Groups Projects
mapvisualization.py 114 KiB
Newer Older
  • Learn to ignore specific revisions
  •             colorRampItems.append(QgsColorRampShader.ColorRampItem(value, color))
            shaderFunc.setColorRampItemList(colorRampItems)
            shader = QgsRasterShader()
            shader.setMaximumValue(cmax)
            shader.setMinimumValue(cmin)
            shader.setRasterShaderFunction(shaderFunc)
            r.setShader(shader)
            return r
    
    class DatumViewUI(QFrame, loadUI('timeseriesdatumview.ui')):
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
        """
        Widget to host the MapCanvases of all map views that relate to a single Datum-Sensor combinbation.
        """
        def __init__(self, title='<#>', parent=None):
            super(DatumViewUI, self).__init__(parent)
            self.setupUi(self)
    
        def sizeHint(self):
            m = self.layout().contentsMargins()
    
            s = QSize(0, 0)
    
            map = None
            widgets = [self.layout().itemAt(i).widget() for i in range(self.layout().count())]
            widgets = [w for w in widgets if isinstance(w, QWidget)]
    
            maps = [w for w in widgets if isinstance(w, MapCanvas)]
            others = [w for w in widgets if not isinstance(w, MapCanvas)]
    
            s = self.layout().spacing()
            m = self.layout().contentsMargins()
    
            def totalHeight(widgetList):
                total = QSize(0,0)
                for w in widgetList:
                    ws = w.size()
                    if ws.width() == 0:
                        ws = w.sizeHint()
    
                    if w.isVisible():
                        total.setWidth(max([total.width(), ws.width()]))
                        total.setHeight(total.height() +  ws.height())
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
                return total
    
            baseSize = totalHeight(widgets)
            if baseSize.width() == 0:
                for o in others:
                    baseSize.setWidth(9999)
            s = QSize(baseSize.width() + m.left() + m.right(),
                      baseSize.height() + m.top() + m.bottom())
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            return s
    
    """
        def sizeHint(self):
    
            if not self.ui.isVisible():
                return QSize(0,0)
            else:
                #return self.ui.sizeHint()
    
                size = self.ui.sizeHint()
                s = self.ui.layout().spacing()
                m = self.ui.layout().contentsMargins()
                dx = m.left() + m.right() + s
                dy = self.ui.layout().spacing()
    
                n = len([m for m in self.mapCanvases.keys() if m.isVisible()])
                if n > 0:
                    baseSize = self.mapCanvases.values()[0].size()
                    size = QSize(baseSize.width()+ dx, \
                                 size.height()+ (n+1)*(dy+2*s))
                else:
                    s = ""
                return size
    
    
    """
    
    
    
    
    class DatumView(QObject):
    
    
        sigRenderProgress = pyqtSignal(int,int)
        sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
        sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
        sigVisibilityChanged = pyqtSignal(bool)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, timeSeriesDatum, stv, parent=None):
    
            assert isinstance(timeSeriesDatum, TimeSeriesDatum)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
    
    
            super(DatumView, self).__init__()
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            self.ui = DatumViewUI(parent=parent)
    
            self.showLoading(False)
    
            self.wOffset = self.ui.layout().count()-1
    
            #self.minWidth = 50
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
            self.STV = stv
    
            self.TSD = timeSeriesDatum
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.scrollArea = stv.scrollArea
    
            self.mSensor = self.TSD.sensor
            self.mSensor.sigNameChanged.connect(lambda :self.setColumnInfo())
    
            self.TSD.sigVisibilityChanged.connect(self.setVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.MVC = stv.MVC
            self.DVC = stv.DVC
    
            self.mRenderState = dict()
    
            labelTxt = '{}\n{}'.format(str(self.TSD.date), self.TSD.sensor.name())
            tooltip = '{}'.format(self.TSD.pathImg)
    
        def setVisibility(self, b):
            self.ui.setVisible(b)
            self.sigVisibilityChanged.emit(b)
    
    
        def setHighlighted(self, b=True, timeout=1000):
            styleOn = """.QFrame {
                        border: 4px solid red;
                        border-radius: 4px;
                    }"""
            styleOff = """"""
            if b is True:
                self.ui.setStyleSheet(styleOn)
                if timeout > 0:
                    QTimer.singleShot(timeout, lambda : self.setHighlighted(b=False))
            else:
                self.ui.setStyleSheet(styleOff)
    
    
        def removeMapView(self, mapView):
            canvas = self.mapCanvases.pop(mapView)
    
            self.ui.layout().removeWidget(canvas)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.adjustBaseMinSize()
    
            if self.ui.isVisible():
                for c in self.mapCanvases.values():
                    if c.isVisible():
    
    
        def insertMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            from timeseriesviewer.mapcanvas import MapCanvas
    
            mapCanvas = MapCanvas(self.ui)
    
            mapCanvas.setObjectName('MapCanvas {} {}'.format(mapView.title(), self.TSD.date))
    
            mapCanvas.blockSignals(True)
    
            mapCanvas.setMapView(mapView)
            mapCanvas.setTSD(self.TSD)
    
            self.registerMapCanvas(mapView, mapCanvas)
    
            # register MapCanvas on MV level
    
            mapView.registerMapCanvas(self.mSensor, mapCanvas)
    
            # register MapCanvas on STV level
            self.STV.registerMapCanvas(mapCanvas)
    
            mapCanvas.blockSignals(False)
    
            mapCanvas.renderComplete.connect(lambda : self.onRenderingChange(False))
            mapCanvas.renderStarting.connect(lambda : self.onRenderingChange(True))
    
    
                lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('Map {}'.format(self.mSensor.name()), dt))
    
            mapCanvas.sigDataLoadingFinished.connect(
                lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('All Sensors', dt))
    
    
        def showLoading(self, b):
            if b:
                self.ui.progressBar.setRange(0,0)
                self.ui.progressBar.setValue(-1)
            else:
                self.ui.progressBar.setRange(0,1)
                self.ui.progressBar.setValue(0)
    
        def onRenderingChange(self, b):
            mc = self.sender()
            #assert isinstance(mc, QgsMapCanvas)
            self.mRenderState[mc] = b
            self.showLoading(any(self.mRenderState.values()))
    
        def onRendering(self, *args):
            renderFlags = [m.renderFlag() for m in self.mapCanvases.values()]
            drawFlags = [m.isDrawing() for m in self.mapCanvases.values()]
    
            isLoading = any(renderFlags)
    
            self.showLoading(isLoading)
    
            s = ""
    
        def registerMapCanvas(self, mapView, mapCanvas):
    
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
    
            assert isinstance(mapView, MapView)
    
            self.mapCanvases[mapView] = mapCanvas
    
            #mapView.sigTitleChanged.connect(lambda title : mapCanvas.setSaveFileName('{}_{}'.format(self.TSD.date, title)))
    
            mapCanvas.layerModel().setRasterLayerSources([self.TSD.pathImg])
    
    
            self.ui.layout().insertWidget(self.wOffset + len(self.mapCanvases), mapCanvas)
    
            self.ui.update()
    
            #register signals handled on (this) DV level
            mapCanvas.renderStarting.connect(lambda: self.sigLoadingStarted.emit(mapView, self.TSD))
            mapCanvas.mapCanvasRefreshed.connect(lambda: self.sigLoadingFinished.emit(mapView, self.TSD))
    
            mapCanvas.sigShowProfiles.connect(lambda c, t : mapView.sigShowProfiles.emit(c,mapCanvas, t))
    
            mapCanvas.sigChangeDVRequest.connect(self.onMapCanvasRequest)
    
        def onMapCanvasRequest(self, mapCanvas, key):
    
            if key == 'hide_date':
                self.TSD.setVisibility(False)
    
            if key == 'copy_sensor':
                QApplication.clipboard().setText(self.TSD.sensor.name())
            if key == 'copy_date':
                QApplication.clipboard().setText(str(self.TSD.date))
            if key == 'copy_path':
                QApplication.clipboard().setText(str(self.TSD.pathImg))
    
        def __lt__(self, other):
            assert isinstance(other, DatumView)
            return self.TSD < other.TSD
    
            assert isinstance(other, DatumView)
            return self.TSD == other.TSD
    
        sigLoadingStarted = pyqtSignal(DatumView, MapView)
        sigLoadingFinished = pyqtSignal(DatumView, MapView)
    
        sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    
        sigShowMapLayerInfo = pyqtSignal(dict)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
    
        sigMapSizeChanged = pyqtSignal(QSize)
    
        sigCRSChanged = pyqtSignal(QgsCoordinateReferenceSystem)
    
        sigActivateMapTool = pyqtSignal(str)
    
    
        def __init__(self, timeSeriesViewer):
            super(SpatialTemporalVisualization, self).__init__()
    
            #assert isinstance(timeSeriesViewer, TimeSeriesViewer), timeSeriesViewer
    
            self.mBlockCanvasSignals = False
    
            self.mSpatialExtent = SpatialExtent.world()
    
            self.mCRS = self.mSpatialExtent.crs()
            self.mSize = QSize(200,200)
            self.mColor = Qt.black
    
            self.mMapCanvases = []
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            #map-tool handling
            self.mMapToolActivator = None
            self.mMapTools = []
    
            assert isinstance(self.scrollArea, MapViewScrollArea)
    
    
    
            self.mRefreshTimer = QTimer(self)
            self.mRefreshTimer.setInterval(1000)
            self.mRefreshTimer.timeout.connect(self.refresh)
    
            self.scrollArea.sigResized.connect(self.mRefreshTimer.start)
            self.scrollArea.horizontalScrollBar().valueChanged.connect(self.mRefreshTimer.start)
    
            self.TSV = timeSeriesViewer
            self.TS = timeSeriesViewer.TS
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.dockMapViews.setTimeSeries(self.TS)
    
            self.targetLayout = self.ui.scrollAreaSubsetContent.layout()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.MVC = MapViewCollection(self)
            #self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.MVC = self.ui.dockMapViews
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            assert isinstance(self.MVC, MapViewCollectionDock)
    
            self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.vectorOverlay = None
    
    
            self.DVC = DateViewCollection(self)
    
            self.DVC.sigResizeRequired.connect(self.adjustScrollArea)
    
            #self.DVC.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
            #self.DVC.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
    
            #self.timeSeriesDateViewCollection.sigSpatialExtentChanged.connect(self.setSpatialExtent)
            self.TS.sigTimeSeriesDatesAdded.connect(self.DVC.addDates)
            self.TS.sigTimeSeriesDatesRemoved.connect(self.DVC.removeDates)
    
            self.DVC.addDates(self.TS[:])
    
            if len(self.TS) > 0:
                self.setSpatialExtent(self.TS.getMaxSpatialExtent())
    
            #self.setSubsetSize(QSize(100,50))
    
        def registerMapCanvas(self, mapCanvas):
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
    
    
            self.mMapCanvases.append(mapCanvas)
    
            #set general canvas properties
            mapCanvas.setFixedSize(self.mSize)
            mapCanvas.setCrs(self.mCRS)
            mapCanvas.setSpatialExtent(self.mSpatialExtent)
    
    
            #register on map canvas signals
            mapCanvas.sigSpatialExtentChanged.connect(lambda e: self.setSpatialExtent(e, mapCanvas))
    
    
    
    
            from timeseriesviewer.mapcanvas import MapCanvas
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setCrosshairStyle(crosshairStyle)
    
            #self.MVC.setCrosshairStyle(crosshairStyle)
    
    
        def setShowCrosshair(self, b):
            self.MVC.setShowCrosshair(b)
    
        def setVectorLayer(self, lyr):
            self.MVC.setVectorLayer(lyr)
    
    
    
    
        def setMapSize(self, size):
    
            self.mSize = size
    
            from timeseriesviewer.mapcanvas import MapCanvas
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setFixedSize(size)
    
            self.sigMapSizeChanged.emit(self.mSize)
    
        def subsetSize(self):
            return QSize(self.mSize)
    
    
            #print('STV REFRESH')
    
            for tsdView in self.DVC:
    
            self.mRefreshTimer.stop()
    
    
        def adjustScrollArea(self):
            #adjust scroll area widget to fit all visible widgets
            m = self.targetLayout.contentsMargins()
    
            nX = len(self.DVC)
    
            tsdViews = [v for v in self.DVC if v.ui.isVisible()]
    
            mapViews = [v for v in self.MVC if v.isVisible()]
            nX = len(tsdViews)
            nY = len(mapViews)
    
            spacing = self.targetLayout.spacing()
            margins = self.targetLayout.contentsMargins()
    
            sizeX = 1
            sizeY = 50
            if nX > 0:
                s = tsdViews[0].ui.sizeHint().width()
                s = nX * (s + spacing) + margins.left() + margins.right()
                sizeX = s
    
                    s = tsdViews[0].ui.sizeHint().height()
                    s = s + margins.top() + margins.bottom()
    
                #s = tsdViews[0].ui.sizeHint()
                #s = QSize(nX * (s.width() + spacing) + margins.left() + margins.right(),
                #          s.height() + margins.top() + margins.bottom())
    
            self.targetLayout.parentWidget().setFixedSize(QSize(sizeX, sizeY))
    
        def setMapTool(self, mapToolKey, *args, **kwds):
            # filter map tools
            self.mMapToolActivator = self.sender()
            del self.mMapTools[:]
    
            from timeseriesviewer.mapcanvas import MapTools, CursorLocationMapTool, SpectralProfileMapTool, TemporalProfileMapTool
            for canvas in self.mMapCanvases:
                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(lambda c, m : self.sigShowProfiles.emit(c,m, mapToolKey))
    
            return self.mMapTools
    
        def setSpatialCenter(self, center, mapCanvas0=None):
            if self.mBlockCanvasSignals:
                return True
    
            assert isinstance(center, SpatialPoint)
            center = center.toCrs(self.mCRS)
            if not isinstance(center, SpatialPoint):
                return
    
            self.mBlockCanvasSignals = True
            self.mSpatialExtent.setCenter(center)
            for mapCanvas in self.mMapCanvases:
                if mapCanvas != mapCanvas0:
                    oldState = mapCanvas.blockSignals(True)
                    mapCanvas.setCenter(center)
                    mapCanvas.blockSignals(oldState)
            self.mBlockCanvasSignals = False
    
            self.sigSpatialExtentChanged.emit(self.mSpatialExtent)
    
    
    
        def setSpatialCenter(self, center, mapCanvas0=None):
            if self.mBlockCanvasSignals:
                return True
    
            assert isinstance(center, SpatialPoint)
            center = center.toCrs(self.mCRS)
            if not isinstance(center, SpatialPoint):
                return None
    
            self.mBlockCanvasSignals = True
    
            for mapCanvas in self.mMapCanvases:
                if mapCanvas != mapCanvas0:
                    oldState = mapCanvas.blockSignals(True)
                    mapCanvas.setCenter(center)
                    mapCanvas.blockSignals(oldState)
    
            self.mBlockCanvasSignals = False
            #for mapCanvas in self.mMapCanvases:
            #    mapCanvas.refresh()
            self.mRefreshTimer.start()
    
    
        def setSpatialExtent(self, extent, mapCanvas0=None):
            if self.mBlockCanvasSignals:
                return True
    
            assert isinstance(extent, SpatialExtent)
            extent = extent.toCrs(self.mCRS)
            if not isinstance(extent, SpatialExtent) \
                or extent.isEmpty() or not extent.isFinite() \
                or extent.width() <= 0 \
                or extent.height() <= 0 \
                or extent == self.mSpatialExtent:
    
    
            self.mBlockCanvasSignals = True
            self.mSpatialExtent = extent
            for mapCanvas in self.mMapCanvases:
                if mapCanvas != mapCanvas0:
                    oldState = mapCanvas.blockSignals(True)
                    mapCanvas.setExtent(extent)
                    mapCanvas.blockSignals(oldState)
    
            self.mBlockCanvasSignals = False
    
            #for mapCanvas in self.mMapCanvases:
            #    mapCanvas.refresh()
    
            self.mRefreshTimer.start()
    
            self.sigSpatialExtentChanged.emit(extent)
    
        def setBackgroundColor(self, color):
            assert isinstance(color, QColor)
            self.mColor = color
    
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setCanvasColor(color)
                mapCanvas.refresh()
    
    
        def backgroundColor(self):
            return self.mColor
    
    
        def mapCanvasIterator(self):
            return self.mMapCanvases[:]
    
        def setCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
    
    
            if self.mCRS != crs:
    
                from timeseriesviewer.utils import saveTransform
                if saveTransform(self.mSpatialExtent, self.mCRS, crs):
                    self.mCRS = crs
                    for mapCanvas in self.mapCanvasIterator():
    
                        #print(('STV set CRS {} {}', str(mapCanvas), self.mCRS.description()))
    
                        mapCanvas.setCrs(crs)
                else:
                    pass
    
                self.sigCRSChanged.emit(self.mCRS)
    
    
    
        def crs(self):
    
    
    
        def navigateToTSD(self, TSD):
            assert isinstance(TSD, TimeSeriesDatum)
            #get widget related to TSD
    
            tsdv = self.DVC.tsdView(TSD)
    
            assert isinstance(self.scrollArea, QScrollArea)
            self.scrollArea.ensureWidgetVisible(tsdv.ui)
    
    
    class DateViewCollection(QObject):
    
    
        sigResizeRequired = pyqtSignal()
        sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
        sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
    
        sigShowProfiles = pyqtSignal(SpatialPoint)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
    
    
        def __init__(self, STViz):
            assert isinstance(STViz, SpatialTemporalVisualization)
    
            super(DateViewCollection, self).__init__()
    
            self.STV = STViz
            self.ui = self.STV.targetLayout.parentWidget()
    
            self.scrollArea = self.ui.parentWidget().parentWidget()
            #potentially there are many more dates than views.
            #therefore we implement the addinng/removing of mapviews here
            #we reduce the number of layout refresh calls by
            #suspending signals, adding the new map view canvases, and sending sigResizeRequired
    
    
            self.STV.MVC.sigMapViewAdded.connect(self.addMapView)
            self.STV.MVC.sigMapViewRemoved.connect(self.removeMapView)
    
        def tsdFromMapCanvas(self, mapCanvas):
            assert isinstance(mapCanvas, MapCanvas)
            for view in self.views:
                assert isinstance(view, DatumView)
                if mapCanvas in view.mapCanvases.values():
                    return view.TSD
            return None
    
    
        def tsdView(self, tsd):
            r = [v for v in self.views if v.TSD == tsd]
            if len(r) == 1:
                return r[0]
            else:
                raise Exception('TSD not in list')
    
        def addMapView(self, mapView):
            assert isinstance(mapView, MapView)
            w = self.ui
            w.setUpdatesEnabled(False)
            for tsdv in self.views:
                tsdv.ui.setUpdatesEnabled(False)
    
            for tsdv in self.views:
                tsdv.insertMapView(mapView)
    
            for tsdv in self.views:
                tsdv.ui.setUpdatesEnabled(True)
    
    
            #mapView.sigSensorRendererChanged.connect(lambda *args : self.setRasterRenderer(mapView, *args))
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            mapView.sigMapViewVisibility.connect(lambda: self.sigResizeRequired.emit())
    
            mapView.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.sigResizeRequired.emit()
    
        def removeMapView(self, mapView):
            assert isinstance(mapView, MapView)
            for tsdv in self.views:
                tsdv.removeMapView(mapView)
            self.sigResizeRequired.emit()
    
    
    
        def highlightDate(self, tsd):
            """
            Highlights a time series data for a specific time our
            :param tsd:
            :return:
            """
            tsdView = self.tsdView(tsd)
            if isinstance(tsdView, DatumView):
                tsdView.setHighlight(True)
    
    
        def setFocusView(self, tsd):
            self.focusView = tsd
    
        def orderedViews(self):
            #returns the
            if self.focusView is not None:
    
                assert isinstance(self.focusView, DatumView)
    
                return sorted(self.views,key=lambda v: np.abs(v.TSD.date - self.focusView.TSD.date))
            else:
                return self.views
    
    
        def setSubsetSize(self, size):
            assert isinstance(size, QSize)
            self.subsetSize = size
    
            for tsdView in self.orderedViews():
                tsdView.blockSignals(True)
    
            for tsdView in self.orderedViews():
                tsdView.setSubsetSize(size)
    
            for tsdView in self.orderedViews():
                tsdView.blockSignals(False)
    
    
    
        def addDates(self, tsdList):
            """
            Create a new TSDView
            :param tsdList:
            :return:
            """
            for tsd in tsdList:
                assert isinstance(tsd, TimeSeriesDatum)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                DV = DatumView(tsd, self.STV, parent=self.ui)
    
                #tsdView.setSubsetSize(self.subsetSize)
    
                DV.sigLoadingStarted.connect(self.sigLoadingStarted.emit)
                DV.sigLoadingFinished.connect(self.sigLoadingFinished.emit)
                DV.sigVisibilityChanged.connect(lambda: self.STV.adjustScrollArea())
    
                for i, mapView in enumerate(self.STV.MVC):
                    DV.insertMapView(mapView)
    
                bisect.insort(self.views, DV)
                i = self.views.index(DV)
    
                DV.ui.setParent(self.STV.targetLayout.parentWidget())
                self.STV.targetLayout.insertWidget(i, DV.ui)
                DV.ui.show()
    
    
            if len(tsdList) > 0:
                self.sigResizeRequired.emit()
    
        def removeDates(self, tsdList):
            toRemove = [v for v in self.views if v.TSD in tsdList]
            removedDates = []
    
            for DV in toRemove:
                self.views.remove(DV)
    
    
                for mapCanvas in DV.mapCanvases.values():
                    toRemove = mapCanvas.layers()
                    mapCanvas.setLayers([])
                    toRemove = [l for l in toRemove if isinstance(l, QgsRasterLayer)]
                    if len(toRemove) > 0:
    
                        QgsProject.instance().removeMapLayers([l.id() for l in toRemove])
    
                DV.ui.parent().layout().removeWidget(DV.ui)
                DV.ui.hide()
                DV.ui.close()
                removedDates.append(DV.TSD)
                del DV
    
    
            if len(removedDates) > 0:
                self.sigResizeRequired.emit()
    
        def __len__(self):
            return len(self.views)
    
        def __iter__(self):
            return iter(self.views)
    
        def __getitem__(self, slice):
            return self.views[slice]
    
        def __delitem__(self, slice):
            self.removeDates(self.views[slice])
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    class MapViewListModel(QAbstractListModel):
        """
        A model to keep a list of map views.
    
        """
        sigMapViewsAdded = pyqtSignal(list)
        sigMapViewsRemoved = pyqtSignal(list)
    
        def __init__(self, parent=None):
            super(MapViewListModel, self).__init__(parent)
            self.mMapViewList = []
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def addMapView(self, mapView):
            i = len(self.mMapViewList)
            self.insertMapView(i, mapView)
    
        def insertMapView(self, i, mapView):
            self.insertMapViews(i, [mapView])
    
        def insertMapViews(self, i, mapViews):
            assert isinstance(mapViews, list)
            assert i >= 0 and i <= len(self.mMapViewList)
    
            self.beginInsertRows(QModelIndex(), i, i + len(mapViews) - 1)
    
            for j in range(len(mapViews)):
                mapView = mapViews[j]
                assert isinstance(mapView, MapView)
                mapView.sigTitleChanged.connect(
                    lambda : self.doRefresh([mapView])
                )
                self.mMapViewList.insert(i + j, mapView)
            self.endInsertRows()
            self.sigMapViewsAdded.emit(mapViews)
    
    
        def doRefresh(self, mapViews):
            for mapView in mapViews:
                idx = self.mapView2idx(mapView)
                self.dataChanged.emit(idx, idx)
    
        def removeMapView(self, mapView):
            self.removeMapViews([mapView])
    
        def removeMapViews(self, mapViews):
            assert isinstance(mapViews, list)
            for mv in mapViews:
                assert mv in self.mMapViewList
                idx = self.mapView2idx(mv)
                self.beginRemoveRows(idx.parent(), idx.row(), idx.row())
                self.mMapViewList.remove(mv)
                self.endRemoveRows()
            self.sigMapViewsRemoved.emit(mapViews)
    
        def rowCount(self, parent=None, *args, **kwargs):
            return len(self.mMapViewList)
    
        def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
            return 1
    
    
        def idx2MapView(self, index):
            if isinstance(index, QModelIndex):
                if index.isValid():
                    index = index.row()
                else:
                    return None
            assert index >= 0 and index < len(self.mMapViewList)
            return self.mMapViewList[index]
    
    
        def mapView2idx(self, mapView):
            assert isinstance(mapView, MapView)
            row = self.mMapViewList.index(mapView)
            return self.createIndex(row, 0, mapView)
    
        def __len__(self):
            return len(self.mMapViewList)
    
        def __iter__(self):
            return iter(self.mMapViewList)
    
    
        def __getitem__(self, slice):
            return self.mMapViewList[slice]
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def data(self, index, role=Qt.DisplayRole):
            if not index.isValid():
                return None
    
            if (index.row() >= len(self.mMapViewList)) or (index.row() < 0):
                return None
    
            mapView = self.idx2MapView(index)
            assert isinstance(mapView, MapView)
    
            value = None
    
            if role == Qt.DisplayRole:
                value = '{} {}'.format(index.row() +1 , mapView.title())
            #if role == Qt.DecorationRole:
                #value = classInfo.icon(QSize(20,20))
            if role == Qt.UserRole:
                value = mapView
            return value
    
    class MapViewCollectionDock(QgsDockWidget, loadUI('mapviewdock.ui')):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        sigMapViewAdded = pyqtSignal(MapView)
        sigMapViewRemoved = pyqtSignal(MapView)
    
        sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    
        sigMapCanvasColorChanged = pyqtSignal(QColor)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
        sigCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem)
        sigMapSizeChanged = pyqtSignal(QSize)
    
    
        def setTimeSeries(self, timeSeries):
    
            assert isinstance(timeSeries, TimeSeries)
            self.TS = timeSeries
            self.TS.sigSensorAdded.connect(self.addSensor)
            self.TS.sigSensorRemoved.connect(self.removeSensor)
    
    
        def __init__(self, parent=None):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            super(MapViewCollectionDock, self).__init__(parent)
    
            self.setupUi(self)
    
    
            self.mMapViews = MapViewListModel()
    
            self.baseTitle = self.windowTitle()
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.btnAddMapView.setDefaultAction(self.actionAddMapView)
    
            self.btnRemoveMapView.setDefaultAction(self.actionRemoveMapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.btnRefresh.setDefaultAction(self.actionApplyStyles)
    
            self.btnHighlightMapView.setDefaultAction(self.actionHighlightMapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.btnCrs.crsChanged.connect(self.sigCrsChanged)
            self.btnMapCanvasColor.colorChanged.connect(self.sigMapCanvasColorChanged)
            self.btnApplySizeChanges.clicked.connect(lambda : self.sigMapSizeChanged.emit(QSize(self.spinBoxMapSizeX.value(),self.spinBoxMapSizeY.value())))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.actionAddMapView.triggered.connect(self.createMapView)
    
            self.actionRemoveMapView.triggered.connect(lambda : self.removeMapView(self.currentMapView()) if self.currentMapView() else None)
            self.actionHighlightMapView.triggered.connect(lambda : self.currentMapView().setHighlighted(True) if self.currentMapView() else None)
            self.actionApplyStyles.triggered.connect(self.refreshCurrentMapView)
            #self.actionApplyStyles.triggered.connect(self.dummySlot)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMapViews.sigMapViewsRemoved.connect(self.onMapViewsRemoved)
            self.mMapViews.sigMapViewsAdded.connect(self.onMapViewsAdded)
    
            self.mMapViews.sigMapViewsAdded.connect(self.updateButtons)
            self.mMapViews.sigMapViewsRemoved.connect(self.updateButtons)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.cbMapView.setModel(self.mMapViews)
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            self.cbMapView.currentIndexChanged[int].connect(lambda i : None if i < 0 else self.setCurrentMapView(self.mMapViews.idx2MapView(i)) )
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    
            self.spinBoxMapSizeX.valueChanged.connect(lambda: self.onMapSizeChanged('X'))
            self.spinBoxMapSizeY.valueChanged.connect(lambda: self.onMapSizeChanged('Y'))
            self.mLastMapSize = self.mapSize()
            #self.mapSize() #inits mLastMapSize
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.TS = None
    
    
        def setCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
            self.btnCrs.setCrs(crs)
            self.btnCrs.setLayerCrs(crs)
    
    
        def setMapSize(self, size):
            assert isinstance(size, QSize)
            ws = [self.spinBoxMapSizeX, self.spinBoxMapSizeY]
            oldSize = self.mapSize()
            b = oldSize != size
            for w in ws:
                w.blockSignals(True)
    
            self.spinBoxMapSizeX.setValue(size.width()),
            self.spinBoxMapSizeY.setValue(size.height())
            self.mLastMapSize = QSize(size)
            for w in ws:
                w.blockSignals(False)
            self.mLastMapSize = QSize(size)
            if b:
                self.sigMapSizeChanged.emit(size)
    
        def onMapSizeChanged(self, dim):
            newSize = self.mapSize()
            #1. set size of other dimension accordingly
            if dim is not None:
                if self.checkBoxKeepSubsetAspectRatio.isChecked():
                    if dim == 'X':
                        vOld = self.mLastMapSize.width()
                        vNew = newSize.width()
                        targetSpinBox = self.spinBoxMapSizeY
                    elif dim == 'Y':
                        vOld = self.mLastMapSize.height()
                        vNew = newSize.height()
                        targetSpinBox = self.spinBoxMapSizeX
    
                    oldState = targetSpinBox.blockSignals(True)
                    targetSpinBox.setValue(int(round(float(vNew) / vOld * targetSpinBox.value())))
                    targetSpinBox.blockSignals(oldState)
                    newSize = self.mapSize()
                if newSize != self.mLastMapSize:
                    self.btnApplySizeChanges.setEnabled(True)
            else:
                self.sigMapSizeChanged.emit(self.mapSize())
                self.btnApplySizeChanges.setEnabled(False)
            self.setMapSize(newSize)
    
        def mapSize(self):
            return QSize(self.spinBoxMapSizeX.value(),
                         self.spinBoxMapSizeY.value())
    
    
    
        def refreshCurrentMapView(self, *args):
            mv = self.currentMapView()
            if isinstance(mv, MapView):
                mv.refreshMapView()
            else:
                s  =""
        def dummySlot(self):
            s  =""
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapViewsRemoved(self, mapViews):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for mapView in mapViews:
                idx = self.stackedWidget.indexOf(mapView.ui)
                if idx >= 0:
                    self.stackedWidget.removeWidget(mapView.ui)
                    mapView.ui.close()
                else:
                    s = ""
    
    
            self.actionRemoveMapView.setEnabled(len(self.mMapViews) > 0)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapViewsAdded(self, mapViews):
            nextShown = None
            for mapView in mapViews:
    
                mapView.sigTitleChanged.connect(self.updateTitle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                self.stackedWidget.addWidget(mapView.ui)
                if nextShown is None:
                    nextShown = mapView
    
                contents = mapView.ui.scrollAreaWidgetContents
                size = contents.size()
                hint = contents.sizeHint()
                #mapView.ui.scrollArea.update()
                s = ""
                #setMinimumSize(mapView.ui.scrollAreaWidgetContents.sizeHint())
                #hint = contents.sizeHint()
                #contents.setMinimumSize(hint)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if isinstance(nextShown, MapView):
                self.setCurrentMapView(nextShown)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def updateButtons(self, *args):
            b = len(self.mMapViews) > 0
            self.actionRemoveMapView.setEnabled(b)
            self.actionApplyStyles.setEnabled(b)
            self.actionHighlightMapView.setEnabled(b)
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def createMapView(self):
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapView = MapView(self)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            n = len(self.mMapViews) + 1
            title = 'Map View {}'.format(n)
            while title in [m.title() for m in self.mMapViews]:
                n += 1
                title = 'Map View {}'.format(n)
            mapView.setTitle(title)
    
            mapView.sigShowProfiles.connect(self.sigShowProfiles)