Skip to content
Snippets Groups Projects
mapvisualization.py 67.4 KiB
Newer Older
Benjamin Jakimow's avatar
Benjamin Jakimow committed
            DV.ui.parent().layout().removeWidget(DV.ui)
            DV.ui.hide()
            DV.ui.close()
            removedDates.append(DV.TSD)
            del DV
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        if len(removedDates) > 0:
            self.sigResizeRequired.emit()
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __len__(self):
        return len(self.mViews)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __iter__(self):
        return iter(self.mViews)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __getitem__(self, slice):
        return self.mViews[slice]
Benjamin Jakimow's avatar
Benjamin Jakimow committed
    def __delitem__(self, slice):
        self.removeDates(self.mViews[slice])
Benjamin Jakimow's avatar
Benjamin Jakimow committed
class MapViewListModel(QAbstractListModel):
    """
    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.mapViews()
        self.toolBox.setCurrentWidget(mapView)
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):
            title = '{} | {}'.format(self.baseTitle, mapView.title())
        else:
            title = self.baseTitle
        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

    sigVisibleDatesChanged = pyqtSignal(list)

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.mMapToolKey = MapTools.Pan
        self.mMapToolMode = None
Benjamin Jakimow's avatar
Benjamin Jakimow committed

Benjamin Jakimow's avatar
Benjamin Jakimow committed
        self.scrollArea = self.ui.scrollAreaSubsets
        assert isinstance(self.scrollArea, MapViewScrollArea)
        self.scrollArea.horizontalScrollBar().valueChanged.connect(self.onVisibleMapsChanged)
        self.scrollArea.horizontalScrollBar().rangeChanged.connect(self.onVisibleMapsChanged)
        # self.scrollArea.sigResized.connect(self.onVisibleMapsChanged)
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

    def setMapTool(self, mapToolKey):


        mode = None

        if mapToolKey == MapTools.SelectFeature:
            if self.ui.optionSelectFeaturesRectangle.isChecked():
                mode = QgsMapToolSelectionHandler.SelectionMode.SelectSimple
            elif self.ui.optionSelectFeaturesPolygon.isChecked():
                mode = QgsMapToolSelectionHandler.SelectionMode.SelectPolygon
            elif self.ui.optionSelectFeaturesFreehand.isChecked():
                mode = QgsMapToolSelectionHandler.SelectionMode.SelectFreehand
            elif self.ui.optionSelectFeaturesRadius.isChecked():
                mode = QgsMapToolSelectionHandler.SelectionMode.SelectRadius
            else:
                mode = QgsMapToolSelectionHandler.SelectionMode.SelectSimple

        self.mMapToolKey = mapToolKey
        self.mMapToolMode = mode


        from .mapcanvas import MapCanvas, MapCanvasMapTools

        for canvas in self.mapCanvases():

            if isinstance(canvas, MapCanvas):

                mapTools = canvas.mapTools()
                mapTools.activate(mapToolKey)

                mt = canvas.mapTool()
                if isinstance(mt, QgsMapToolSelect):
                    mt.setSelectionMode(mode)


    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()
        if not isinstance(c, QgsMapCanvas):
            return

        tsvCenter = self.spatialExtent().spatialCenter()
        qgsCenter = SpatialExtent.fromMapCanvas(c).spatialCenter()

        if qgisChanged:
            if tsvCenter.crs().isValid():
                self.mSyncLock = True
                qgsCenter = qgsCenter.toCrs(tsvCenter.crs())
                if isinstance(qgsCenter, SpatialPoint):
                    self.setSpatialCenter(qgsCenter)
        else:
            # change QGIS
            if qgsCenter.crs().isValid():
                self.mSyncLock = True
                tsvCenter = tsvCenter.toCrs(qgsCenter.crs())
                if isinstance(tsvCenter, SpatialPoint):
                    c.setCenter(tsvCenter)
    def visibleMaps(self)->list:
        """
        Returns a list of mapcanvas visible to the user
        :return: [list-of-MapCanvases
        """
        return [m for m in self.mapCanvases() if m.isVisibleToViewport()]

    def onVisibleMapsChanged(self, *args):

        visibleDates = set([m.tsd() for m in self.visibleMaps()])
        if visibleDates != self.mVisibleDates:
            self.mVisibleDates.clear()
            self.mVisibleDates.update(visibleDates)
            self.sigVisibleDatesChanged.emit(list(self.mVisibleDates))
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
        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)

        mapCanvas.sigSpatialExtentChanged.connect(self.setSpatialExtent)
Benjamin Jakimow's avatar
Benjamin Jakimow committed
        mapCanvas.sigCrosshairPositionChanged.connect(self.onCrosshairChanged)

        # activate the current map tool
        mapTools = mapCanvas.mapTools()
        mapTools.activate(self.mMapToolKey)

        mt = mapCanvas.mapTool()
        if isinstance(mt, QgsMapToolSelect):
            mt.setSelectionMode(self.mMapToolMode)

Benjamin Jakimow's avatar
Benjamin Jakimow committed
    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 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)