Skip to content
Snippets Groups Projects
mapvisualization.py 90.6 KiB
Newer Older
  • Learn to ignore specific revisions
  •     def mapViews(self)->list:
            """
            Returns a list of all mapviews
            :return [list-of-MapViews]:
            """
            return self.MVC[:]
    
    
        def setCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
    
    
            if self.mCRS != crs:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                transform = QgsCoordinateTransform()
                transform.setSourceCrs(self.mCRS)
                transform.setDestinationCrs(crs)
                if transform.isValid() and not transform.isShortCircuited():
    
                    self.mCRS = crs
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                        # print(('STV set CRS {} {}', str(mapCanvas), self.mCRS.description()))
                        mapCanvas.setDestinationCrs(QgsCoordinateReferenceSystem(crs))
                    """
                    from timeseriesviewer.utils import saveTransform
                    if saveTransform(self.mSpatialExtent, self.mCRS, crs):
                        self.mCRS = crs
                        
                    else:
                        pass
                    """
                    self.sigCRSChanged.emit(self.crs())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def crs(self)->QgsCoordinateReferenceSystem:
            """
            Returns the QgsCoordinateReferenceSystem
            :return: QgsCoordinateReferenceSystem
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def spatialExtent(self)->SpatialExtent:
            """
            Returns the SpatialExtent
            :return: SpatialExtent
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def navigateToTSD(self, TSD:TimeSeriesDatum):
            """
            Changes the viewport of the scroll window to show the requested TimeSeriesDatum
            :param TSD: TimeSeriesDatum
            """
    
            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.mViews = list()
    
            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.mViews:
    
                assert isinstance(view, DatumView)
    
                if mapCanvas in view.mMapCanvases.values():
    
            r = [v for v in self.mViews 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.mViews:
            #    tsdv.ui.setUpdatesEnabled(False)
    
            for tsdv in self.mViews:
    
            #for tsdv in self.mViews:
            #    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)
    
            #w.setUpdatesEnabled(True)
    
            self.sigResizeRequired.emit()
    
        def removeMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            for tsdv in self.mViews:
    
        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.mViews, key=lambda v: np.abs(v.TSD.date - self.focusView.TSD.date))
    
                return self.mViews
    
        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.mViews, DV)
                i = self.mViews.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.mViews if v.TSD in tsdList]
    
            for DV in toRemove:
    
                self.mViews.remove(DV)
    
                for mapCanvas in DV.mMapCanvases.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.mViews)
    
            return iter(self.mViews)
    
            return self.mViews[slice]
    
            self.removeDates(self.mViews[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
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if isinstance(crs, QgsCoordinateReferenceSystem):
                old = self.btnCrs.crs()
                if old != crs:
                    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
    
    
            for mapView in mapViews:
                self.sigMapViewAdded.emit(mapView)
    
        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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMapViews.addMapView(mapView)
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            return mapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def removeMapView(self, mapView):
    
            if isinstance(mapView, MapView):
                assert mapView in self.mMapViews
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                i = self.mMapViews.mapView2idx(mapView)
                if not i == self.stackedWidget.indexOf(mapView.ui):
                    s = ""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                self.mMapViews.removeMapView(mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
                self.sigMapViewRemoved.emit(mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __len__(self):
            return len(self.mMapViews)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __iter__(self):
            return iter(self.mMapViews)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def __getitem__(self, slice):
            return self.mMapViews[slice]
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __contains__(self, mapView):
            return mapView in self.mMapViews
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def index(self, mapView):
            assert isinstance(mapView, MapView)
            return self.mMapViews.index(mapView)
    
        def setVectorLayer(self, lyr):
            for mapView in self.mMapViews:
                assert isinstance(mapView, MapView)
                mapView.setVectorLayer(lyr)
    
        def addSensor(self, sensor):
            for mapView in self.mMapViews:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def removeSensor(self, sensor):
            for mapView in self.mMapViews:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def applyStyles(self):
            for mapView in self.mMapViews:
                mapView.applyStyles()
    
        def setCrosshairStyle(self, crosshairStyle):
            for mapView in self.mMapViews:
                mapView.setCrosshairStyle(crosshairStyle)
    
        def setShowCrosshair(self, b):
            for mapView in self.mMapViews:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def index(self, mapView):
            assert isinstance(mapView, MapView)
            return self.mapViewsDefinitions.index(mapView)
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setCurrentMapView(self, mapView):
            assert isinstance(mapView, MapView) and mapView in self.mMapViews
            idx = self.stackedWidget.indexOf(mapView.ui)
            if idx >= 0:
                self.stackedWidget.setCurrentIndex(idx)
                self.cbMapView.setCurrentIndex(self.mMapViews.mapView2idx(mapView).row())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.updateTitle()
    
        def updateTitle(self, *args):
            # self.btnToggleMapViewVisibility.setChecked(mapView)
            mapView = self.currentMapView()
            if isinstance(mapView, MapView):
                if mapView in self.mMapViews:
                    i = str(self.mMapViews.mapView2idx(mapView).row()+1)
                else:
                    i = ''
                #title = '{} | {} "{}"'.format(self.baseTitle, i, mapView.title())
                title = '{} | {}'.format(self.baseTitle, i)
                self.setWindowTitle(title)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def currentMapView(self):
    
            if len(self.mMapViews) == 0:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                return None
            else:
                i = self.cbMapView.currentIndex()
    
                if i >= 0:
                    return self.mMapViews.idx2MapView(i)
                else:
                    return None