Skip to content
Snippets Groups Projects
mapvisualization.py 65.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        A model to store a list of map views.
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        """
        sigMapViewsAdded = pyqtSignal(list)
        sigMapViewsRemoved = pyqtSignal(list)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def insertMapView(self, i, mapView):
            self.insertMapViews(i, [mapView])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def insertMapViews(self, i, mapViews):
            assert isinstance(mapViews, list)
            assert i >= 0 and i <= len(self.mMapViewList)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.beginInsertRows(QModelIndex(), i, i + len(mapViews) - 1)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def doRefresh(self, mapViews):
            for mapView in mapViews:
                idx = self.mapView2idx(mapView)
                self.dataChanged.emit(idx, idx)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def removeMapView(self, mapView):
            self.removeMapViews([mapView])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def rowCount(self, parent=None, *args, **kwargs):
            return len(self.mMapViewList)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
            return 1
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapView2idx(self, mapView):
            assert isinstance(mapView, MapView)
            row = self.mMapViewList.index(mapView)
            return self.createIndex(row, 0, mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __len__(self):
            return len(self.mMapViewList)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __iter__(self):
            return iter(self.mMapViewList)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if (index.row() >= len(self.mMapViewList)) or (index.row() < 0):
                return None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapView = self.idx2MapView(index)
            assert isinstance(mapView, MapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            value = None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            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
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class MapViewDock(QgsDockWidget, loadUI('mapviewdock.ui')):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        sigMapViewAdded = pyqtSignal(MapView)
        sigMapViewRemoved = pyqtSignal(MapView)
        sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        sigMapCanvasColorChanged = pyqtSignal(QColor)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
        sigCrsChanged = pyqtSignal(QgsCoordinateReferenceSystem)
        sigMapSizeChanged = pyqtSignal(QSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setTimeSeries(self, timeSeries:TimeSeries):
            assert isinstance(timeSeries, TimeSeries)
            self.mTimeSeries = timeSeries
            self.mTimeSeries.sigSensorAdded.connect(self.addSensor)
            self.mTimeSeries.sigSensorRemoved.connect(self.removeSensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, parent=None):
            super(MapViewDock, self).__init__(parent)
            self.setupUi(self)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            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.btnCrs.crsChanged.connect(self.sigCrsChanged)
            self.btnMapCanvasColor.colorChanged.connect(self.onMapCanvasBackgroundColorChanged)
            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.actionApplyStyles.triggered.connect(self.refreshCurrentMapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.toolBox.currentChanged.connect(self.onToolboxIndexChanged)
    
    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()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mTimeSeries = None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapViews(self)->list:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns the defined MapViews
            :return: [list-of-MapViews]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(self.toolBox, QToolBox)
            mapViews = []
            for i in range(self.toolBox.count()):
                item = self.toolBox.widget(i)
                if isinstance(item, MapView):
                    mapViews.append(item)
            return mapViews
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapCanvases(self)->list:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns all MapCanvases from all MapViews
            :return: [list-of-MapCanvases]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            maps = []
            for mapView in self.mapViews():
                assert isinstance(mapView, MapView)
                maps.extend(mapView.mapCanvases())
            return maps
    
        def setCrs(self, crs):
    
    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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    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)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapSize(self):
            return QSize(self.spinBoxMapSizeX.value(),
                         self.spinBoxMapSizeY.value())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def refreshCurrentMapView(self, *args):
            mv = self.currentMapView()
            if isinstance(mv, MapView):
                mv.refreshMapView()
            else:
                s  =""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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 = ""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.actionRemoveMapView.setEnabled(len(self.mMapViews) > 0)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapBackgroundColor(self)->QColor:
            """
            Returns the map canvas background color
            :return: QColor
            """
            return self.btnMapCanvasColor.color()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setMapBackgroundColor(self, color:QColor):
            """
            Sets the MapCanvas background color
            :param color: QColor
            """
            if color != self.mapBackgroundColor():
                self.btnMapCanvasColor.setColor(color)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapCanvasBackgroundColorChanged(self, color:QColor):
            """
            Reacts on a changes map color
            :param color: QColor
            """
            assert isinstance(color, QColor)
            self.mColor = color
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.addToRefreshPipeLine(color)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.sigMapCanvasColorChanged.emit(color)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapViewsAdded(self, mapViews):
            nextShown = None
            for mapView in mapViews:
                mapView.sigTitleChanged.connect(self.updateTitle)
                self.stackedWidget.addWidget(mapView.ui)
                if nextShown is None:
                    nextShown = mapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                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)
            if isinstance(nextShown, MapView):
                self.setCurrentMapView(nextShown)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            for mapView in mapViews:
                self.sigMapViewAdded.emit(mapView)
    
    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)->MapView:
            """
            Create a new MapView
            :return: MapView
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapView = MapView()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            n = len(self.mapViews()) + 1
            title = 'Map View {}'.format(n)
            while title in [m.title() for m in self.mapViews()]:
                n += 1
                title = 'Map View {}'.format(n)
            mapView.setTitle(title)
            mapView.sigShowProfiles.connect(self.sigShowProfiles)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapView.setTimeSeries(self.mTimeSeries)
            self.addMapView(mapView)
            return mapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
        def addMapView(self, mapView:MapView):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Adds a MapView
            :param mapView: MapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(mapView, MapView)
            mapView.sigTitleChanged.connect(lambda *args, mv=mapView : self.onMapViewUpdated(mv))
            mapView.sigMapViewVisibility.connect(lambda *args, mv=mapView : self.onMapViewUpdated(mv))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            i = self.toolBox.addItem(mapView, mapView.windowIcon(), mapView.title())
            self.toolBox.setCurrentIndex(i)
            self.onMapViewUpdated(mapView)
            self.sigMapViewAdded.emit(mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onToolboxIndexChanged(self):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            b = isinstance(self.toolBox.currentWidget(), MapView)
            self.actionRemoveMapView.setEnabled(b)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapViewUpdated(self, mapView:MapView):
            """
            Handles updates that react on MapView changes
            :param mapView: MapView to make the update for
            """
            numMV = 0
            for i in range(self.toolBox.count()):
                item = self.toolBox.widget(i)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if isinstance(item, MapView):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    numMV += 1
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if item == mapView:
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    if mapView.isVisible():
                        icon = QIcon(":/timeseriesviewer/icons/mapview.svg")
                    else:
                        icon = QIcon(":/timeseriesviewer/icons/mapviewHidden.svg")
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    self.toolBox.setItemIcon(i, icon)
                    self.toolBox.setItemText(i, 'Map View {} "{}"'.format(numMV, mapView.title()))
                    break
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def removeMapView(self, mapView:MapView)->MapView:
            """
            Removes a MapView
            :param mapView: MapView
            :return: MapView
            """
            assert mapView in self.mapViews()
            for i in range(self.toolBox.count()):
                w = self.toolBox.widget(i)
                if isinstance(w, MapView) and w == mapView:
                    self.toolBox.removeItem(i)
                    mapView.close()
                    if self.toolBox.count() >= i:
                        self.toolBox.setCurrentIndex(min(i, self.toolBox.count()-1))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.sigMapViewRemoved.emit(mapView)
            return mapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __len__(self)->int:
            """
            Returns the number of MapViews
            :return: int
            """
            return len(self.mapViews())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Provides an iterator over all MapViews
            :return:
            """
            return iter(self.mapViews())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.mapViews()[slice]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __contains__(self, mapView):
            return mapView in self.mapViews()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def index(self, mapView):
            assert isinstance(mapView, MapView)
            return self.mapViews().index(mapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def addSensor(self, sensor:SensorInstrument):
            """
            Adds a new SensorInstrument
            :param sensor: SensorInstrument
            """
            for mapView in self.mapViews():
                mapView.addSensor(sensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def removeSensor(self, sensor:SensorInstrument):
            """
            Removes a Sensor
            :param sensor: SensorInstrument
            """
            for mapView in self.mMapViews:
                mapView.removeSensor(sensor)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def applyStyles(self):
            for mapView in self.mMapViews:
                mapView.applyStyles()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setCrosshairStyle(self, crosshairStyle):
            for mapView in self.mMapViews:
                mapView.setCrosshairStyle(crosshairStyle)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setShowCrosshair(self, b):
            for mapView in self.mMapViews:
                mapView.setCrosshairVisibility(b)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    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
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.updateTitle()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        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
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def currentMapView(self):
            w = self.toolBox.currentWidget()
            if isinstance(w, MapView):
                return w
            return None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class SpatialTemporalVisualization(QObject):
        """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        """
        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)
        sigMapViewAdded = pyqtSignal(MapView)
        sigMapViewRemoved = pyqtSignal(MapView)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def __init__(self, timeSeriesViewer):
            super(SpatialTemporalVisualization, self).__init__()
            # assert isinstance(timeSeriesViewer, TimeSeriesViewer), timeSeriesViewer
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # default map settings
            self.mSpatialExtent = SpatialExtent.world()
            self.mCRS = self.mSpatialExtent.crs()
            self.mSize = QSize(200, 200)
            self.mColor = Qt.black
            self.mMapCanvases = []
            self.ui = timeSeriesViewer.ui
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # map-tool handling
            self.mMapTools = []
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.scrollArea = self.ui.scrollAreaSubsets
            assert isinstance(self.scrollArea, MapViewScrollArea)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            # self.scrollArea.sigResized.connect(self.refresh())
            # self.scrollArea.horizontalScrollBar().valueChanged.connect(self.mRefreshTimer.start)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.TSV = timeSeriesViewer
            self.TS = timeSeriesViewer.timeSeries()
            self.ui.dockMapViews.setTimeSeries(self.TS)
            self.targetLayout = self.ui.scrollAreaSubsetContent.layout()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.MVC = self.ui.dockMapViews
            assert isinstance(self.MVC, MapViewDock)
            self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
            self.MVC.sigMapViewAdded.connect(self.onMapViewAdded)
            self.MVC.sigMapViewAdded.connect(self.sigMapViewAdded.emit)
            self.MVC.sigMapViewRemoved.connect(self.sigMapViewRemoved.emit)
            self.vectorOverlay = None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.DVC = DateViewCollection(self)
            self.DVC.sigResizeRequired.connect(self.adjustScrollArea)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.TS.sigTimeSeriesDatesAdded.connect(self.DVC.addDates)
            self.TS.sigTimeSeriesDatesRemoved.connect(self.DVC.removeDates)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.DVC.addDates(self.TS[:])
            if len(self.TS) > 0:
                self.setSpatialExtent(self.TS.maxSpatialExtent())
            #self.setSubsetSize(QSize(100,50))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMapRefreshTimer = QTimer(self)
            self.mMapRefreshTimer.timeout.connect(self.timedCanvasRefresh)
            self.mMapRefreshTimer.setInterval(500)
            self.mMapRefreshTimer.start()
            self.mNumberOfHiddenMapsToRefresh = 2
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            self.mSyncLock = False
    
        def setCurrentLayer(self, layer):
    
            assert layer is None or isinstance(layer, QgsMapLayer)
            self.mCurrentLayer = layer
            for canvas in self.mapCanvases():
                assert isinstance(canvas, QgsMapCanvas)
                canvas.setCurrentLayer(layer)
    
        def syncQGISCanvasCenter(self, qgisChanged:bool):
    
            if self.mSyncLock:
                return
    
            iface = qgis.utils.iface
            assert isinstance(iface, QgisInterface)
    
            c = iface.mapCanvas()
            assert isinstance(c, QgsMapCanvas)
            tsvCenter = self.spatialExtent().spatialCenter()
            qgsCenter = SpatialExtent.fromMapCanvas(c).spatialCenter()
    
            if qgisChanged:
                # change TSV
                if tsvCenter.crs().isValid():
                    self.mSyncLock = True
                    qgsCenter = qgsCenter.toCrs(tsvCenter.crs())
                    self.setSpatialCenter(qgsCenter)
            else:
                # change QGIS
                if qgsCenter.crs().isValid():
                    self.mSyncLock = True
                    tsvCenter = tsvCenter.toCrs(qgsCenter.crs())
                    c.setCenter(tsvCenter)
                else:
                    pass
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def timedCanvasRefresh(self, *args, force:bool=False):
    
            self.mSyncLock = False
    
            # do refresh maps
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(self.scrollArea, MapViewScrollArea)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            visibleMaps = [m for m in self.mapCanvases() if m.isVisibleToViewport()]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            hiddenMaps = sorted([m for m in self.mapCanvases() if not m.isVisibleToViewport()],
                                key = lambda c : self.scrollArea.distanceToCenter(c) )
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            n = 0
            # redraw all visible maps
            for c in visibleMaps:
                assert isinstance(c, MapCanvas)
                c.timedRefresh()
                n += 1
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if n < 10:
                # refresh up to mNumberOfHiddenMapsToRefresh maps which are not visible to the user
                i = 0
                for c in hiddenMaps:
                    assert isinstance(c, MapCanvas)
                    c.timedRefresh()
                    i += 1
                    if i >= self.mNumberOfHiddenMapsToRefresh and not force:
                        break
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapViewFromCanvas(self, mapCanvas:MapCanvas)->MapView:
            """
            Returns the MapView a mapCanvas belongs to
            :param mapCanvas: MapCanvas
            :return: MapView
            """
            for mapView in self.MVC:
                assert isinstance(mapView, MapView)
                if mapCanvas in mapView.mapCanvases():
                    return mapView
            return None
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onMapViewAdded(self, *args):
            self.adjustScrollArea()
            s = ""
        def createMapView(self)->MapView:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Create a new MapWiew
            :return: MapView
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.MVC.createMapView()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def registerMapCanvas(self, mapCanvas:MapCanvas):
            """
            Connects a MapCanvas and its signals
            :param mapCanvas: MapCanvas
            """
            from eotimeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
    
            mapCanvas.setMapLayerStore(self.TSV.mMapLayerStore)
    
            mapCanvas.sigCrosshairPositionChanged.connect(lambda point, canvas=mapCanvas: self.TSV.setCurrentLocation(point, canvas))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMapCanvases.append(mapCanvas)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.setFixedSize(self.mSize)
            mapCanvas.setDestinationCrs(self.mCRS)
            mapCanvas.setSpatialExtent(self.mSpatialExtent)
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            def onChanged(e, mapCanvas0=None):
    
                #self.setSpatialExtent(e, mapCanvas0=mapCanvas0)
                self.setSpatialExtent(e)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #mapCanvas.sigSpatialExtentChanged.connect(lambda e: self.setSpatialExtent(e, mapCanvas0=mapCanvas))
    
            #mapCanvas.sigSpatialExtentChanged.connect(lambda e: onChanged(e, mapCanvas0=mapCanvas))
            mapCanvas.sigSpatialExtentChanged.connect(self.setSpatialExtent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.sigCrosshairPositionChanged.connect(self.onCrosshairChanged)
    
        def onCrosshairChanged(self, spatialPoint:SpatialPoint):
            """
            Synchronizes all crosshair positions. Takes care of CRS differences.
            :param spatialPoint: SpatialPoint of new Crosshair position
            """
            from eotimeseriesviewer import CrosshairStyle
    
            srcCanvas = self.sender()
            if isinstance(srcCanvas, MapCanvas):
                dstCanvases = [c for c in self.mapCanvases() if c != srcCanvas]
            else:
                dstCanvases = [c for c in self.mapCanvases()]
    
            if isinstance(spatialPoint, SpatialPoint):
                for mapCanvas in dstCanvases:
                    mapCanvas.setCrosshairPosition(spatialPoint, emitSignal=False)
    
    
        def setCrosshairStyle(self, crosshairStyle:CrosshairStyle):
            """
            Sets a crosshair style to all map canvas
            :param crosshairStyle: CrosshairStyle
    
            """
            for mapView in self.mapViews():
                assert isinstance(mapView, MapView)
                mapView.setCrosshairStyle(crosshairStyle)
    
    
        def setCrosshairVisibility(self, b:bool):
            """
            Sets the Crosshair visiblity
            :param b: bool
            """
            assert isinstance(b, bool)
            self.onCrosshairChanged(b)
    
    
        def setVectorLayer(self, lyr:QgsVectorLayer):
            """
            Sets a QgsVectorLaye to be shown on top of raster images
            :param lyr: QgsVectorLayer
            """
            self.MVC.setVectorLayer(lyr)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mSize = size
            from eotimeseriesviewer.mapcanvas import MapCanvas
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setFixedSize(size)
            self.sigMapSizeChanged.emit(self.mSize)
            self.adjustScrollArea()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapSize(self)->QSize:
            """
            Returns the MapCanvas size
            :return: QSize
            """
            return QSize(self.mSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def refresh(self):
            """
            Refreshes all visible MapCanvases
            """
            for c in self.mapCanvases():
                assert isinstance(c, MapCanvas)
                c.refresh()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.mMapRefreshTimer.stop()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def adjustScrollArea(self):
            """
            Adjusts the scroll area widget to fit all visible widgets
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            m = self.targetLayout.contentsMargins()
            nX = len(self.DVC)
            w = h = 0
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            s = QSize()
            r = None
            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()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            sizeX = 1
            sizeY = 50
            if nX > 0:
                s = tsdViews[0].ui.sizeHint().width()
                s = nX * (s + spacing) + margins.left() + margins.right()
                sizeX = s
            if nY > 0 and nX > 0:
                    s = tsdViews[0].ui.sizeHint().height()
                    s = s + margins.top() + margins.bottom()
                    sizeY = s
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                #s = tsdViews[0].ui.sizeHint()
                #s = QSize(nX * (s.width() + spacing) + margins.left() + margins.right(),
                #          s.height() + margins.top() + margins.bottom())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #print(sizeX, sizeY)
            self.targetLayout.parentWidget().resize(QSize(sizeX, sizeY))
    
        def setMapTool(self, mapToolKey, *args, **kwds):
            """
            Create a maptool instance to each MapCanvas
            :param mapToolKey: str which MapTool is to create, or QgsMapTool instance
            :param args: optional maptool arguments
            :param kwds: optional maptool keywords
            :return: [list-of-QgsMapTools]
            """
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            del self.mMapTools[:]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            from eotimeseriesviewer import MapTools, CursorLocationMapTool, SpectralProfileMapTool, TemporalProfileMapTool
            for canvas in self.mMapCanvases:
                mt = None
                if mapToolKey in MapTools.mapToolKeys():
                    mt = MapTools.create(mapToolKey, canvas, *args, **kwds)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                if isinstance(mapToolKey, QgsMapTool):
                    mt = MapTools.copy(mapToolKey, canvas, *args, **kwds)
    
                if isinstance(mt, QgsMapTool):
                    canvas.setMapTool(mt)
                    self.mMapTools.append(mt)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    # if required, link map-tool with specific slots
                    if isinstance(mt, CursorLocationMapTool):
                        mt.sigLocationRequest[SpatialPoint, QgsMapCanvas].connect(self.onLocationRequest)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.mMapTools
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def onLocationRequest(self, pt:SpatialPoint, canvas:QgsMapCanvas):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.sigShowProfiles.emit(pt, canvas, "")
    
        def setSpatialCenter(self, center:SpatialPoint, mapCanvas0=None):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Sets the spatial center of all MapCanvases
            :param center: SpatialPoint
            :param mapCanvas0:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(center, SpatialPoint)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            if isinstance(extent, SpatialExtent):
                centerOld = extent.center()
                center = center.toCrs(extent.crs())
                if center != centerOld:
                    extent = extent.__copy__()
                    extent.setCenter(center)
                    self.setSpatialExtent(extent)
    
        def spatialCenter(self)->SpatialPoint:
            return self.spatialExtent().spatialCenter()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setSpatialExtent(self, extent, mapCanvas0=None):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Sets the spatial extent of all MapCanvases
            :param extent: SpatialExtent
            :param mapCanvas0:
            :return:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(extent, SpatialExtent)
    
            extent = extent.toCrs(self.crs())
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if not isinstance(extent, SpatialExtent) \
                or extent.isEmpty() or not extent.isFinite() \
                or extent.width() <= 0 \
                or extent.height() <= 0 \
                or extent == self.mSpatialExtent:
                return
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if self.mSpatialExtent == extent:
                return
    
            self.mSpatialExtent = extent
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
    
            if lastExtent != extent:
                self.sigSpatialExtentChanged.emit(extent)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapViewDock(self)->MapViewDock:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns the MapViewDock that controls all MapViews
            :return: MapViewDock
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.MVC
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setMapBackgroundColor(self, color:QColor):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Sets the MapCanvas background color
            :param color: QColor
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(self.MVC, MapViewDock)
            self.MVC.setMapBackgroundColor(color)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapCanvases(self, mapView=None)->list:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns MapCanvases
            :param mapView: a MapView to return MapCanvases from only, defaults to None
            :return: [list-of-MapCanvas]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if isinstance(mapView, MapView):
                s = ""
            return self.mMapCanvases[:]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapViews(self)->list:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            Returns a list of all mapviews
            :return [list-of-MapViews]:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            return self.MVC[:]
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setCrs(self, crs):
            assert isinstance(crs, QgsCoordinateReferenceSystem)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if self.mCRS != crs:
                transform = QgsCoordinateTransform()
                transform.setSourceCrs(self.mCRS)
                transform.setDestinationCrs(crs)
                if transform.isValid() and not transform.isShortCircuited():
                    self.mCRS = crs
                    for mapCanvas in self.mapCanvases():
                        # 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
            """
            return self.mCRS
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def spatialExtent(self)->SpatialExtent:
            """
            Returns the SpatialExtent
            :return: SpatialExtent
            """
            return self.mSpatialExtent
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
    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)