Skip to content
Snippets Groups Projects
mapvisualization.py 90.6 KiB
Newer Older
    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)
        #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[:]