Skip to content
Snippets Groups Projects
mapvisualization.py 90.7 KiB
Newer Older
  • Learn to ignore specific revisions
  •     def sensorWidget(self, sensor):
    
            return self.mSensorViews[sensor]
    
    def displayBandNames(provider_or_dataset, bands=None):
        results = None
        if isinstance(provider_or_dataset, QgsRasterLayer):
            return displayBandNames(provider_or_dataset.dataProvider())
        elif isinstance(provider_or_dataset, QgsRasterDataProvider):
            if provider_or_dataset.name() == 'gdal':
                ds = gdal.Open(provider_or_dataset.dataSourceUri())
                results = displayBandNames(ds, bands=bands)
            else:
                # same as in QgsRasterRendererWidget::displayBandName
                results = []
                if bands is None:
                    bands = range(1, provider_or_dataset.bandCount() + 1)
                for band in bands:
                    result = provider_or_dataset.generateBandName(band)
                    colorInterp ='{}'.format(provider_or_dataset.colorInterpretationName(band))
                    if colorInterp != 'Undefined':
                        result += '({})'.format(colorInterp)
                    results.append(result)
    
        elif isinstance(provider_or_dataset, gdal.Dataset):
            results = []
            if bands is None:
                bands = range(1, provider_or_dataset.RasterCount+1)
            for band in bands:
                b = provider_or_dataset.GetRasterBand(band)
                descr = b.GetDescription()
                if len(descr) == 0:
                    descr = 'Band {}'.format(band)
                results.append(descr)
    
        return results
    
    
    class RasterDataProviderMockup(QgsRasterDataProvider):
    
        def __init__(self):
            super(RasterDataProviderMockup, self).__init__('')
    
    
    
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    class MapViewRenderSettings(QgsCollapsibleGroupBox, loadUI('mapviewrendersettings.ui')):
    
    
    
        LUT_RENDERER = {QgsMultiBandColorRenderer:QgsMultiBandColorRendererWidget,
                        QgsSingleBandGrayRenderer:QgsSingleBandGrayRendererWidget,
                        QgsSingleBandPseudoColorRenderer:QgsSingleBandPseudoColorRendererWidget,
                        QgsPalettedRasterRenderer:QgsPalettedRendererWidget}
        LUT_RENDERER[QgsMultiBandColorRenderer]=MultiBandColorRendererWidget
        LUT_RENDERER[QgsSingleBandPseudoColorRenderer]=SingleBandPseudoColorRendererWidget
        LUT_RENDERER[QgsSingleBandGrayRenderer]=SingleBandGrayRendererWidget
    
        LUT_RENDERER[QgsPalettedRasterRenderer] = PalettedRendererWidget
    
        def __init__(self, sensor, parent=None):
            """Constructor."""
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            super(MapViewRenderSettings, self).__init__(parent)
    
            # Set up the user interface from Designer.
            # After setupUI you can access any designer object by doing
            # self.<objectname>, and you can use autoconnect slots - see
            # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html
            # #widgets-and-dialogs-with-auto-connect
    
            self.setupUi(self)
    
            QApplication.clipboard().dataChanged.connect(self.onClipboardChanged)
    
            assert isinstance(sensor, SensorInstrument)
            self.mSensor = sensor
            self.mSensor.sigNameChanged.connect(self.onSensorNameChanged)
            self.setTitle(self.mSensor.name())
    
            from timeseriesviewer.models import OptionListModel, Option
            rasterRendererModel = OptionListModel()
            #rasterRendererModel.addOption(Option(QgsMultiBandColorRendererWidget, name='multibandcolor (QGIS)', mRenderType = QgsMultiBandColorRenderer))
            #rasterRendererModel.addOption(Option(QgsPalettedRendererWidget, name='paletted (QGIS)', mRenderType = QgsPalettedRasterRenderer))
            #rasterRendererModel.addOption(Option(QgsSingleBandGrayRendererWidget, name='singlegray (QGIS)', mRenderType = QgsSingleBandGrayRenderer))
            #rasterRendererModel.addOption(Option(QgsSingleBandPseudoColorRendererWidget, name='singlebandpseudocolor (QGIS)', mRenderType = QgsSingleBandPseudoColorRenderer))
    
    
            rasterRendererModel.addOption(Option(MultiBandColorRendererWidget, name='Multibandcolor', mRenderType=QgsMultiBandColorRenderer))
            rasterRendererModel.addOption(Option(SingleBandGrayRendererWidget, name='Singlegray', mRenderType=QgsSingleBandGrayRenderer))
            rasterRendererModel.addOption(Option(SingleBandPseudoColorRendererWidget, name='Singleband Pseudocolor', mRenderType=QgsSingleBandPseudoColorRenderer))
            rasterRendererModel.addOption(Option(PalettedRendererWidget, name='Paletted', mRenderType=QgsPalettedRasterRenderer))
    
            self.mRasterRendererModel = rasterRendererModel
    
            self.cbRenderType.setModel(self.mRasterRendererModel)
            assert isinstance(self.stackedWidget, QStackedWidget)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMockupCanvas.setVisible(False)
            self.mMockupRasterLayer = self.mSensor.mockupLayer()
    
            self.mMockupCanvas.setLayers([self.mMockupRasterLayer])
            for func in rasterRendererModel.optionValues():
    
                #extent = self.canvas.extent()
                #w = func.create(self.mMockupRasterLayer, self.mMockupRasterLayer.extent())
                w = func(self.mMockupRasterLayer, self.mMockupRasterLayer.extent())
    
    
                assert isinstance(w, QgsRasterRendererWidget)
                w.setParent(self.stackedWidget)
    
    
    
                w.setSizePolicy(QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
                w.setMapCanvas(self.mMockupCanvas)
    
                self.stackedWidget.addWidget(w)
    
            self.mMapCanvases = []
    
            self.initActions()
    
        def initActions(self):
    
    
            self.btnPasteStyle.setDefaultAction(self.actionPasteStyle)
            self.btnCopyStyle.setDefaultAction(self.actionCopyStyle)
            self.btnApplyStyle.setDefaultAction(self.actionApplyStyle)
    
            clipboardRenderer = rendererFromXml(QApplication.clipboard().mimeData())
            self.actionPasteStyle.setEnabled(isinstance(clipboardRenderer, QgsRasterRenderer))
            self.actionPasteStyle.triggered.connect(self.pasteStyleFromClipboard)
            self.actionCopyStyle.triggered.connect(self.pasteStyleToClipboard)
    
            self.actionApplyStyle.triggered.connect(self.applyStyle)
    
    
        def mapCanvases(self):
            return self.mMapCanvases[:]
    
        def registerMapCanvas(self, mapCanvas):
    
            assert isinstance(mapCanvas, MapCanvas)
            self.mMapCanvases.append(mapCanvas)
            mapCanvas.sigChangeSVRequest.connect(self.onMapCanvasRendererChangeRequest)
    
    
        def onMapCanvasRendererChangeRequest(self, mapCanvas, renderer):
            self.setRasterRenderer(renderer)
            self.applyStyle()
    
        def onSensorNameChanged(self, newName):
    
            self.setTitle(self.mSensor.name())
            self.actionApplyStyle.setToolTip('Apply style to all map view images from "{}"'.format(self.mSensor.name()))
    
    
    
        def currentRenderWidget(self):
            """
            Returns the current QgsRasterRendererWidget
            :return: QgsRasterRendererWidget
            """
            return self.stackedWidget.currentWidget()
    
    
        def setRasterRenderer(self, renderer):
            assert isinstance(renderer, QgsRasterRenderer)
            assert isinstance(self.stackedWidget, QStackedWidget)
    
            self.mMockupRasterLayer.setRenderer(renderer)
    
            #find the widget class that fits
            cls = None
    
            for option in self.mRasterRendererModel:
                if type(renderer) == option.mRenderType:
                    cls = option.value()
                    break
            if cls == None:
                return
    
            widgets = []
            for i in range(self.stackedWidget.count()):
                w = self.stackedWidget.widget(i)
                if isinstance(w, cls):
                    widgets.append(w)
    
            if len(widgets) > 0:
                for w in widgets:
                    assert isinstance(w, QgsRasterRendererWidget)
    
    
                    #w.setRasterLayer(self.mMockupRasterLayer)
                    #w.setFromRenderer(cloneRenderer(renderer))
                    w.setFromRenderer(renderer)
                    #w.doComputations()
    
    
                w = widgets[0]
                self.stackedWidget.setCurrentWidget(w)
    
                self.cbRenderType.setCurrentIndex(self.stackedWidget.currentIndex())
    
    
        def rasterRenderer(self):
    
            return self.stackedWidget.currentWidget().renderer()
    
    
        def apply(self):
    
            mRendererWidget = self.currentRenderWidget()
            mRendererWidget.doComputations()
    
        def onClipboardChanged(self):
            mimeData = QApplication.clipboard().mimeData()
            renderer = rendererFromXml(mimeData)
            b = isinstance(renderer, QgsRasterRenderer)
            #if b == False:
            #    print(mimeData.formats())
            #    s = ""
            self.actionPasteStyle.setEnabled(b)
    
    
    
        def pasteStyleFromClipboard(self):
            mimeData = QApplication.clipboard().mimeData()
            renderer = rendererFromXml(mimeData)
            if isinstance(renderer, QgsRasterRenderer):
                self.setRasterRenderer(renderer)
    
        def pasteStyleToClipboard(self):
            xml = rendererToXml(self.rasterRenderer())
            assert isinstance(xml, QDomDocument)
            md = QMimeData()
            #['application/qgis.style', 'text/plain']
    
            md.setData('application/qgis.style', xml.toByteArray())
            md.setData('text/plain', xml.toByteArray())
            QApplication.clipboard().setMimeData(md)
    
        def applyStyle(self, *args):
    
            r = self.rasterRenderer()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            if isinstance(r, QgsRasterRenderer):
                for mapCanvas in self.mapCanvases():
                    assert isinstance(mapCanvas, MapCanvas)
                    mapCanvas.addToRefreshPipeLine(MapCanvas.Command.RefreshRenderer)
    
    
    
    
    
    RENDER_CLASSES = {}
    RENDER_CLASSES['rasterrenderer'] = {
    
        'singlebandpseudocolor': QgsSingleBandPseudoColorRenderer,
    
        'singlebandgray': QgsSingleBandGrayRenderer,
    
        'paletted': QgsPalettedRasterRenderer,
    
        'multibandcolor': QgsMultiBandColorRenderer,
        'hillshade': QgsHillshadeRenderer
    }
    RENDER_CLASSES['renderer-v2'] = {
        'categorizedSymbol':QgsCategorizedSymbolRenderer,
        'singleSymbol':QgsSingleSymbolRenderer
    }
    
    
    
    def rendererToXml(renderer):
    
        """
        Returns a renderer XML representation
        :param renderer: QgsRasterRender | QgsFeatureRenderer
        :return: QDomDocument
        """
    
        doc = QDomDocument()
    
        if isinstance(renderer, QgsRasterRenderer):
    
            #create a dummy raster layer
            import uuid
            from timeseriesviewer.virtualrasters import write_vsimem, read_vsimem
            xml = """<VRTDataset rasterXSize="1" rasterYSize="1">
                      <GeoTransform>  0.0000000000000000e+00,  1.0000000000000000e+00,  0.0000000000000000e+00,  0.0000000000000000e+00,  0.0000000000000000e+00, -1.0000000000000000e+00</GeoTransform>
                      <VRTRasterBand dataType="Float32" band="1">
                        <Metadata>
                          <MDI key="STATISTICS_MAXIMUM">0</MDI>
                          <MDI key="STATISTICS_MEAN">0</MDI>
                          <MDI key="STATISTICS_MINIMUM">0</MDI>
                          <MDI key="STATISTICS_STDDEV">0</MDI>
                        </Metadata>
                        <Description>Band 1</Description>
                        <Histograms>
                          <HistItem>
                            <HistMin>0</HistMin>
                            <HistMax>0</HistMax>
                            <BucketCount>1</BucketCount>
                            <IncludeOutOfRange>0</IncludeOutOfRange>
                            <Approximate>0</Approximate>
                            <HistCounts>0</HistCounts>
                          </HistItem>
                        </Histograms>
                      </VRTRasterBand>
                    </VRTDataset>
                    """
    
            path = '/vsimem/{}.vrt'.format(uuid.uuid4())
    
            drv = gdal.GetDriverByName('VRT')
            assert isinstance(drv, gdal.Driver)
            write_vsimem(path, xml)
    
            lyr = QgsRasterLayer(path)
    
            assert lyr.isValid()
    
            lyr.setRenderer(renderer.clone())
    
            #remove dummy raster layer
            lyr = None
            drv.Delete(path)
    
    
        elif isinstance(renderer, QgsFeatureRenderer):
            #todo: distinguish vector type from requested renderer
            lyr = QgsVectorLayer('Point?crs=epsg:4326&field=id:integer', 'dummy', 'memory')
            lyr.setRenderer(renderer.clone())
    
        else:
            raise NotImplementedError()
    
        return doc
    
    
    def rendererFromXml(xml):
        """
        Reads a string `text` and returns the first QgsRasterRenderer or QgsFeatureRenderer (if defined).
        :param text:
        :return:
        """
    
        if isinstance(xml, QMimeData):
            for format in ['application/qgis.style', 'text/plain']:
                if format in xml.formats():
                    dom  = QDomDocument()
                    dom.setContent(xml.data(format))
                    return rendererFromXml(dom)
            return None
    
        elif isinstance(xml, str):
            dom = QDomDocument()
            dom.setContent(xml)
            return rendererFromXml(dom)
    
        assert isinstance(xml, QDomDocument)
        root = xml.documentElement()
        for baseClass, renderClasses in RENDER_CLASSES.items():
            elements = root.elementsByTagName(baseClass)
            if elements.count() > 0:
                elem = elements.item(0).toElement()
                typeName = elem.attributes().namedItem('type').nodeValue()
                if typeName in renderClasses.keys():
                    rClass = renderClasses[typeName]
                    if baseClass == 'rasterrenderer':
    
                        return rClass.create(elem, DUMMY_RASTERINTERFACE)
                    elif baseClass == 'renderer-v2':
                        context = QgsReadWriteContext()
                        return rClass.load(elem, context)
                        #return rClass.create(elem)
                else:
                    print(typeName)
                    s =""
        return None
    
    
    
    
    class DatumViewUI(QFrame, loadUI('timeseriesdatumview.ui')):
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
        """
        Widget to host the MapCanvases of all map views that relate to a single Datum-Sensor combinbation.
        """
        def __init__(self, title='<#>', parent=None):
            super(DatumViewUI, self).__init__(parent)
            self.setupUi(self)
    
        def sizeHint(self):
            m = self.layout().contentsMargins()
    
            s = QSize(0, 0)
    
            map = None
            widgets = [self.layout().itemAt(i).widget() for i in range(self.layout().count())]
            widgets = [w for w in widgets if isinstance(w, QWidget)]
    
            maps = [w for w in widgets if isinstance(w, MapCanvas)]
            others = [w for w in widgets if not isinstance(w, MapCanvas)]
    
            s = self.layout().spacing()
            m = self.layout().contentsMargins()
    
            def totalHeight(widgetList):
                total = QSize(0,0)
                for w in widgetList:
                    ws = w.size()
                    if ws.width() == 0:
                        ws = w.sizeHint()
    
                    if w.isVisible():
                        total.setWidth(max([total.width(), ws.width()]))
                        total.setHeight(total.height() +  ws.height())
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
                return total
    
            baseSize = totalHeight(widgets)
            if baseSize.width() == 0:
                for o in others:
                    baseSize.setWidth(9999)
            s = QSize(baseSize.width() + m.left() + m.right(),
                      baseSize.height() + m.top() + m.bottom())
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            return s
    
    """
        def sizeHint(self):
    
            if not self.ui.isVisible():
                return QSize(0,0)
            else:
                #return self.ui.sizeHint()
    
                size = self.ui.sizeHint()
                s = self.ui.layout().spacing()
                m = self.ui.layout().contentsMargins()
                dx = m.left() + m.right() + s
                dy = self.ui.layout().spacing()
    
                n = len([m for m in self.mapCanvases.keys() if m.isVisible()])
                if n > 0:
                    baseSize = self.mapCanvases.values()[0].size()
                    size = QSize(baseSize.width()+ dx, \
                                 size.height()+ (n+1)*(dy+2*s))
                else:
                    s = ""
                return size
    
    
    """
    
    
    
    
    class DatumView(QObject):
    
    
        sigRenderProgress = pyqtSignal(int,int)
        sigLoadingStarted = pyqtSignal(MapView, TimeSeriesDatum)
        sigLoadingFinished = pyqtSignal(MapView, TimeSeriesDatum)
        sigVisibilityChanged = pyqtSignal(bool)
    
    
        def __init__(self, timeSeriesDatum:TimeSeriesDatum, stv, parent=None):
    
            assert isinstance(timeSeriesDatum, TimeSeriesDatum)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
    
    
            super(DatumView, self).__init__()
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            self.ui = DatumViewUI(parent=parent)
    
            self.showLoading(False)
    
            self.wOffset = self.ui.layout().count()-1
    
            #self.minWidth = 50
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            assert isinstance(stv, SpatialTemporalVisualization)
            self.STV = stv
    
            self.TSD = timeSeriesDatum
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.scrollArea = stv.scrollArea
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mSensor = self.TSD.mSensor
    
            self.mSensor.sigNameChanged.connect(lambda :self.setColumnInfo())
    
            self.TSD.sigVisibilityChanged.connect(self.setVisibility)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.MVC = stv.MVC
            self.DVC = stv.DVC
    
            self.mMapCanvases = dict()
    
            self.mRenderState = dict()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            labelTxt = '{}\n{}'.format(str(self.TSD.mDate), self.TSD.mSensor.name())
            tooltip = '\n'.join([tss.uri()for tss in self.TSD.sources()])
    
        def setVisibility(self, b):
            self.ui.setVisible(b)
            self.sigVisibilityChanged.emit(b)
    
    
        def setHighlighted(self, b=True, timeout=1000):
            styleOn = """.QFrame {
                        border: 4px solid red;
                        border-radius: 4px;
                    }"""
            styleOff = """"""
            if b is True:
                self.ui.setStyleSheet(styleOn)
                if timeout > 0:
                    QTimer.singleShot(timeout, lambda : self.setHighlighted(b=False))
            else:
                self.ui.setStyleSheet(styleOff)
    
    
            canvas = self.mMapCanvases.pop(mapView)
    
            self.ui.layout().removeWidget(canvas)
    
        def mapCanvases(self)->list:
            """
            Retuns the MapCanvases of this DataView
            :return: [list-of-MapCanvases]
            """
            return self.mMapCanvases.values()
    
        def refresh(self):
            """
            Refreshes the MapCanvases in this DatumView, if they are not hidden behind a scroll area.
            """
    
    
        def insertMapView(self, mapView):
            assert isinstance(mapView, MapView)
    
            from timeseriesviewer.mapcanvas import MapCanvas
    
            mapCanvas = MapCanvas(self.ui)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.setObjectName('MapCanvas {} {}'.format(mapView.title(), self.TSD.mDate))
    
            self.registerMapCanvas(mapView, mapCanvas)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.setMapView(mapView)
            mapCanvas.setTSD(self.TSD)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #mapCanvas.setMapView(mapView)
    
            mapView.registerMapCanvas(self.mSensor, mapCanvas)
    
            self.STV.registerMapCanvas(mapCanvas)
    
            mapCanvas.renderComplete.connect(lambda : self.onRenderingChange(False))
            mapCanvas.renderStarting.connect(lambda : self.onRenderingChange(True))
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #mapCanvas.sigMapRefreshed[float, float].connect(
            #    lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('Map {}'.format(self.mSensor.name()), dt))
            #mapCanvas.sigMapRefreshed.connect(
            #    lambda dt: self.STV.TSV.ui.dockSystemInfo.addTimeDelta('All Sensors', dt))
    
        def showLoading(self, b):
            if b:
    
                self.ui.progressBar.setRange(0, 0)
    
                self.ui.progressBar.setValue(-1)
            else:
                self.ui.progressBar.setRange(0,1)
                self.ui.progressBar.setValue(0)
    
        def onRenderingChange(self, b):
            mc = self.sender()
            #assert isinstance(mc, QgsMapCanvas)
            self.mRenderState[mc] = b
            self.showLoading(any(self.mRenderState.values()))
    
        def onRendering(self, *args):
    
            renderFlags = [m.renderFlag() for m in self.mMapCanvases.values()]
            drawFlags = [m.isDrawing() for m in self.mMapCanvases.values()]
    
            isLoading = any(renderFlags)
    
            self.showLoading(isLoading)
    
            s = ""
    
        def registerMapCanvas(self, mapView, mapCanvas):
    
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
    
            assert isinstance(mapView, MapView)
    
            self.mMapCanvases[mapView] = mapCanvas
    
            #mapView.sigTitleChanged.connect(lambda title : mapCanvas.setSaveFileName('{}_{}'.format(self.TSD.date, title)))
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.mapLayerModel().addMapLayerSources(self.TSD.qgsMimeDataUtilsUris())
    
            #self.ui.layout().insertWidget(self.wOffset + len(self.mapCanvases), mapCanvas)
            self.ui.layout().insertWidget(self.ui.layout().count() - 1, mapCanvas)
    
            self.ui.update()
    
            #register signals handled on (this) DV level
            mapCanvas.renderStarting.connect(lambda: self.sigLoadingStarted.emit(mapView, self.TSD))
            mapCanvas.mapCanvasRefreshed.connect(lambda: self.sigLoadingFinished.emit(mapView, self.TSD))
    
            mapCanvas.sigShowProfiles.connect(lambda c, t : mapView.sigShowProfiles.emit(c,mapCanvas, t))
    
            mapCanvas.sigChangeDVRequest.connect(self.onMapCanvasRequest)
    
        def onMapCanvasRequest(self, mapCanvas, key):
    
            if key == 'hide_date':
                self.TSD.setVisibility(False)
    
            if key == 'copy_sensor':
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                QApplication.clipboard().setText(self.TSD.mSensor.name())
    
            if key == 'copy_date':
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                QApplication.clipboard().setText(str(self.TSD.date()))
    
            if key == 'copy_path':
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                QApplication.clipboard().setText('\n'.join(self.TSD.sourceUris()))
    
        def __lt__(self, other):
            assert isinstance(other, DatumView)
            return self.TSD < other.TSD
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            :param other:
            :return:
            """
    
            assert isinstance(other, DatumView)
            return self.TSD == other.TSD
    
        sigLoadingStarted = pyqtSignal(DatumView, MapView)
        sigLoadingFinished = pyqtSignal(DatumView, MapView)
    
        sigShowProfiles = pyqtSignal(SpatialPoint, MapCanvas, str)
    
        sigShowMapLayerInfo = pyqtSignal(dict)
        sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
    
        sigMapSizeChanged = pyqtSignal(QSize)
    
        sigCRSChanged = pyqtSignal(QgsCoordinateReferenceSystem)
    
        sigActivateMapTool = pyqtSignal(str)
    
    
        def __init__(self, timeSeriesViewer):
            super(SpatialTemporalVisualization, self).__init__()
    
            #assert isinstance(timeSeriesViewer, TimeSeriesViewer), timeSeriesViewer
    
            self.mSpatialExtent = SpatialExtent.world()
    
            self.mCRS = self.mSpatialExtent.crs()
    
            self.mColor = Qt.black
    
            self.mMapCanvases = []
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
            #map-tool handling
            self.mMapToolActivator = None
            self.mMapTools = []
    
            assert isinstance(self.scrollArea, MapViewScrollArea)
    
            #self.scrollArea.sigResized.connect(self.refresh())
            #self.scrollArea.horizontalScrollBar().valueChanged.connect(self.mRefreshTimer.start)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.TS = timeSeriesViewer.timeSeries()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.ui.dockMapViews.setTimeSeries(self.TS)
    
            self.targetLayout = self.ui.scrollAreaSubsetContent.layout()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            #self.MVC = MapViewCollection(self)
            #self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.MVC = self.ui.dockMapViews
    
    benjamin.jakimow@geo.hu-berlin.de's avatar
    benjamin.jakimow@geo.hu-berlin.de committed
            assert isinstance(self.MVC, MapViewCollectionDock)
    
            self.MVC.sigShowProfiles.connect(self.sigShowProfiles.emit)
    
            self.MVC.sigMapViewAdded.connect(self.onMapViewAdded)
    
            self.DVC = DateViewCollection(self)
    
            self.DVC.sigResizeRequired.connect(self.adjustScrollArea)
    
            #self.DVC.sigLoadingStarted.connect(self.ui.dockRendering.addStartedWork)
            #self.DVC.sigLoadingFinished.connect(self.ui.dockRendering.addFinishedWork)
    
            #self.timeSeriesDateViewCollection.sigSpatialExtentChanged.connect(self.setSpatialExtent)
            self.TS.sigTimeSeriesDatesAdded.connect(self.DVC.addDates)
            self.TS.sigTimeSeriesDatesRemoved.connect(self.DVC.removeDates)
    
            self.DVC.addDates(self.TS[:])
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                self.setSpatialExtent(self.TS.maxSpatialExtent())
    
            #self.setSubsetSize(QSize(100,50))
    
            self.mMapRefreshTimer = QTimer(self)
            self.mMapRefreshTimer.timeout.connect(self.timedCanvasRefresh)
            self.mMapRefreshTimer.setInterval(500)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            self.mMapRefreshTimer.start()
    
            self.mNumberOfHiddenMapsToRefresh = 2
    
    
    
        def timedCanvasRefresh(self, *args, force:bool=False):
            #do refresh maps
    
            assert isinstance(self.scrollArea, MapViewScrollArea)
    
    
            visibleMaps = [m for m in self.mapCanvases() if m.isVisibleToViewport()]
    
            hiddenMaps = sorted([m for m in self.mapCanvases() if not m.isVisibleToViewport()],
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                                key = lambda c : self.scrollArea.distanceToCenter(c) )
    
    
            n = 0
            #redraw all visible maps
            for c in visibleMaps:
                assert isinstance(c, MapCanvas)
    
                n += 1
    
            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)
    
                    i += 1
                    if i >= self.mNumberOfHiddenMapsToRefresh and not force:
                        break
    
    
    
    
    
    
    
        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
    
    
        def onMapViewAdded(self, *args):
            self.adjustScrollArea()
            s = ""
    
        def registerMapCanvas(self, mapCanvas:MapCanvas):
            """
            Connects a MapCanvas and its signals
            :param mapCanvas: MapCanvas
            """
    
            from timeseriesviewer.mapcanvas import MapCanvas
            assert isinstance(mapCanvas, MapCanvas)
    
    
            mapCanvas.setMapLayerStore(self.TSV.mMapLayerStore)
    
            self.mMapCanvases.append(mapCanvas)
    
            #set general canvas properties
            mapCanvas.setFixedSize(self.mSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            mapCanvas.setDestinationCrs(self.mCRS)
    
            mapCanvas.setSpatialExtent(self.mSpatialExtent)
    
            mapCanvas.setLabelingModel(self.TSV.ui.dockLabeling.mLabelAttributeModel)
    
            #register on map canvas signals
            def onChanged(e, mapCanvas0=None):
                self.setSpatialExtent(e, mapCanvas0=mapCanvas0)
            #mapCanvas.sigSpatialExtentChanged.connect(lambda e: self.setSpatialExtent(e, mapCanvas0=mapCanvas))
            mapCanvas.sigSpatialExtentChanged.connect(lambda e: onChanged(e, mapCanvas0=mapCanvas))
    
            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 timeseriesviewer.crosshair 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):
    
                    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):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Sets the Crosshair visiblity
            :param b: bool
            """
    
            assert isinstance(b, bool)
            self.onCrosshairChanged(b)
    
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def setVectorLayer(self, lyr:QgsVectorLayer):
            """
            Sets a QgsVectorLaye to be shown on top of raster images
            :param lyr: QgsVectorLayer
            """
    
        def setMapSize(self, size):
    
            self.mSize = size
    
            from timeseriesviewer.mapcanvas import MapCanvas
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setFixedSize(size)
    
            self.sigMapSizeChanged.emit(self.mSize)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
        def mapSize(self)->QSize:
            """
            Returns the MapCanvas size
            :return: QSize
            """
    
            return QSize(self.mSize)
    
    
            """
            Refreshes all visible MapCanvases
            """
            for c in self.mapCanvases():
                assert isinstance(c, MapCanvas)
                c.refresh()
    
            #self.mMapRefreshTimer.stop()
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Adjusts the scroll area widget to fit all visible widgets
            """
    
    
            nX = len(self.DVC)
    
            tsdViews = [v for v in self.DVC if v.ui.isVisible()]
    
            mapViews = [v for v in self.MVC if v.isVisible()]
            nX = len(tsdViews)
            nY = len(mapViews)
    
            spacing = self.targetLayout.spacing()
            margins = self.targetLayout.contentsMargins()
    
            sizeX = 1
            sizeY = 50
            if nX > 0:
                s = tsdViews[0].ui.sizeHint().width()
                s = nX * (s + spacing) + margins.left() + margins.right()
                sizeX = s
    
                    s = tsdViews[0].ui.sizeHint().height()
                    s = s + margins.top() + margins.bottom()
    
                #s = tsdViews[0].ui.sizeHint()
                #s = QSize(nX * (s.width() + spacing) + margins.left() + margins.right(),
                #          s.height() + margins.top() + margins.bottom())
    
            self.targetLayout.parentWidget().resize(QSize(sizeX, sizeY))
    
        def setMapTool(self, mapToolKey, *args, **kwds):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            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]
            """
    
            self.mMapToolActivator = self.sender()
            del self.mMapTools[:]
    
    
            from timeseriesviewer.maptools import MapTools, CursorLocationMapTool, SpectralProfileMapTool, TemporalProfileMapTool
    
            for canvas in self.mMapCanvases:
                mt = None
                if mapToolKey in MapTools.mapToolKeys():
                    mt = MapTools.create(mapToolKey, canvas, *args, **kwds)
    
                if isinstance(mapToolKey, QgsMapTool):
                    mt = MapTools.copy(mapToolKey, canvas, *args, **kwds)
    
                if isinstance(mt, QgsMapTool):
                    canvas.setMapTool(mt)
                    self.mMapTools.append(mt)
    
    
                    #if required, link map-tool with specific slots
    
                    if isinstance(mt, CursorLocationMapTool):
                        mt.sigLocationRequest[SpatialPoint, QgsMapCanvas].connect(lambda c, m : self.sigShowProfiles.emit(c,m, mapToolKey))
    
            return self.mMapTools
    
        def setSpatialCenter(self, center, mapCanvas0=None):
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
            """
            Sets the spatial center of all MapCanvases
            :param center: SpatialPoint
            :param mapCanvas0:
            """
    
            assert isinstance(center, SpatialPoint)
            center = center.toCrs(self.mCRS)
            if not isinstance(center, SpatialPoint):
                return
    
    
            self.mSpatialExtent.setCenter(center)
            for mapCanvas in self.mMapCanvases:
                if mapCanvas != mapCanvas0:
                    oldState = mapCanvas.blockSignals(True)
                    mapCanvas.setCenter(center)
                    mapCanvas.blockSignals(oldState)
    
            self.sigSpatialExtentChanged.emit(self.mSpatialExtent)
    
    
    
        def setSpatialCenter(self, center:SpatialPoint, mapCanvas0=None):
            """
            Sets the MapCanvas center.
            :param center: SpatialPoint
            :param mapCanvas0: MapCanvas0 optional
            """
    
    
            assert isinstance(center, SpatialPoint)
            center = center.toCrs(self.mCRS)
            if not isinstance(center, SpatialPoint):
                return None
    
    
    
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
    
                if mapCanvas != mapCanvas0:
    
                    center0 = mapCanvas.spatialCenter()
                    if center0 != center:
                        oldState = mapCanvas.blockSignals(True)
                        mapCanvas.setCenter(center)
                        mapCanvas.blockSignals(oldState)
    
        def setSpatialExtent(self, extent, mapCanvas0=None):
    
            """
            Sets the spatial extent of all MapCanvases
            :param extent: SpatialExtent
            :param mapCanvas0:
            :return:
            """
    
            assert isinstance(extent, SpatialExtent)
            extent = extent.toCrs(self.mCRS)
            if not isinstance(extent, SpatialExtent) \
                or extent.isEmpty() or not extent.isFinite() \
                or extent.width() <= 0 \
                or extent.height() <= 0 \
                or extent == self.mSpatialExtent:
    
            self.mSpatialExtent = extent
    
            for mapCanvas in self.mapCanvases():
                assert isinstance(mapCanvas, MapCanvas)
                extent0 = mapCanvas.spatialExtent()
                if mapCanvas != mapCanvas0 and extent0 != extent:
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
                    mapCanvas.addToRefreshPipeLine(extent)
    
            self.sigSpatialExtentChanged.emit(extent)
    
        def setBackgroundColor(self, color:QColor):
            """
            Sets the MapCanvas background color
            :param color: QColor
            """
    
            assert isinstance(color, QColor)
            self.mColor = color
    
            for mapCanvas in self.mMapCanvases:
                assert isinstance(mapCanvas, MapCanvas)
                mapCanvas.setCanvasColor(color)
    
    Benjamin Jakimow's avatar
    Benjamin Jakimow committed
    
    
        def backgroundColor(self)->QColor:
            """
            Returns the MapCanvas background color
            :return: QColor
            """
    
        def mapCanvases(self, mapView=None)->list:
            """
            Returns MapCanvases
            :param mapView: a MapView to return MapCanvases from only, defaults to None
            :return: [list-of-MapCanvas]
            """
            if isinstance(mapView, MapView):
                s = ""
    
            return self.mMapCanvases[:]