Newer
Older
# -*- coding: utf-8 -*-
"""
/***************************************************************************

Benjamin Jakimow
committed
EO Time Series Viewer
-------------------
begin : 2015-08-20
git sha : $Format:%H$
copyright : (C) 2017 by HU-Berlin
email : benjamin.jakimow@geo.hu-berlin.de
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
"""
# noinspection PyPep8Naming
import os
from qgis.core import *
from qgis.gui import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtXml import QDomDocument

Benjamin Jakimow
committed
from . import SETTINGS
from .utils import *
from .timeseries import TimeSeriesDatum
from .crosshair import CrosshairDialog, CrosshairStyle
from .maptools import *
class MapLayerInfo(object):
"""
A minimum description of a QgsMapLayer source.
"""
def __init__(self, srcOrMapLayer, isVisible, provider='gdal', renderer=None):
self.mSrc = ''
self.mLayer = None
self.mProvider = None
self.mRenderer = None
self.setRenderer(renderer)
if isinstance(srcOrMapLayer, QgsMapLayer):
self.mSrc = srcOrMapLayer.source()
self.mProvider = srcOrMapLayer.providerType()
self.mLayer = srcOrMapLayer
if isinstance(srcOrMapLayer, QgsVectorLayer):
self.mRenderer = srcOrMapLayer.renderer()
elif isinstance(srcOrMapLayer, QgsRasterLayer):
self.mRenderer = srcOrMapLayer.renderer()

Benjamin Jakimow
committed
assert provider in ['ogr','gdal','memory']
self.mIsVisible = isVisible
def isSameSource(self, other):
if not isinstance(other, MapLayerInfo):
return False
return self.mSrc == other.mSrc
def initMapLayer(self):
if self.mProvider == 'gdal':
self.mLayer = QgsRasterLayer(self.mSrc)
elif self.mProvider == 'ogr':
self.mLayer = QgsVectorLayer(self.mSrc,os.path.basename(self.mSrc), 'ogr', True)
self.setRenderer(self.mRenderer)
def setRenderer(self, renderer):
self.mRenderer = renderer
if self.mProvider == 'ogr' and isinstance(renderer, QgsFeatureRenderer) or \
self.mProvider == 'gdal' and isinstance(renderer, QgsRasterRenderer):
self.mRenderer = renderer
if self.isInitialized():
from timeseriesviewer.mapvisualization import cloneRenderer
copyRenderer(self.mRenderer, self.mLayer)
def setIsVisible(self, b):
self.mIsVisible = b
def isVisible(self):
return self.mIsVisible
def layer(self):
"""returns a QgsMapLayer object related to the specified source.
If not provided in the constructor, the QgsMapLayer will be initialized with first call of this method.
"""
if not self.isInitialized():
self.initMapLayer()
return self.mLayer
def isInitialized(self):
return isinstance(self.mLayer, QgsMapLayer)
def isRegistered(self):
if not self.isInitialized():
return None
ref = QgsProject.instance().mapLayer(self.mLayer.layerId())
return isinstance(ref, QgsMapLayer)
class MapCanvasLayerModel(QAbstractTableModel):
"""
A model to save a list of QgsMapLayers and additional properties.
"""
def __init__(self, parent=None):
super(MapCanvasLayerModel, self).__init__(parent=parent)
self.mColumnNames = ['layerID', 'isVisible', '']
self.mLayerInfos = []
def __len__(self):
return len(self.mLayerInfos)
def __iter__(self):
return iter(self.mLayerInfos)
def __repr__(self):
info = 'MapLayerModel::'
for li in self.mLayerInfos:
if li.isVisible():
info += '{}'.format(li.mSrc)
return info
def setRenderer(self, renderer):
for li in self.mLayerInfos:
assert isinstance(li, MapLayerInfo)
li.setRenderer(renderer)
def setVectorLayerSources(self, vectorSources, **kwds):
self.removeLayerInfos(self.vectorLayerInfos())
self.insertLayerInfos(0, vectorSources, provider='ogr', **kwds)
def setVectorLayerVisibility(self, b):
for li in self.vectorLayerInfos():
li.setIsVisible(b)
def setRasterLayerVisibility(self, b):
for li in self.rasterLayerInfos():
li.setIsVisible(b)
def setRasterLayerSources(self, rasterSources, **kwds):
self.removeLayerInfos(self.rasterLayerInfos())
self.addLayerInfos(rasterSources, provider='gdal', **kwds)
def vectorLayerInfos(self):

Benjamin Jakimow
committed
return [li for li in self.mLayerInfos if li.mProvider in ['ogr', 'memory']]
def rasterLayerInfos(self):
return [li for li in self.mLayerInfos if li.mProvider == 'gdal']
def addLayerInfo(self, mapLayer, **kwds):
self.addLayerInfos([mapLayer], **kwds)
def addLayerInfos(self, mapLayers, **kwds):
self.insertLayerInfos(len(self), mapLayers, **kwds)
def insertLayerInfo(self, i, mapLayer, **kwds):
self.insertLayerInfo(i, [mapLayer], **kwds)
def insertLayerInfos(self, i, mapLayers, isVisible=True, provider='gdal'):
for mapLayer in mapLayers:
if isinstance(mapLayer, QgsRasterLayer) and provider != 'gdal':
continue

Benjamin Jakimow
committed
if isinstance(mapLayer, QgsVectorLayer) and provider not in ['ogr', 'memory']:

Benjamin Jakimow
committed
if isinstance(mapLayer, QgsMapLayer):
li = MapLayerInfo(mapLayer, isVisible=isVisible, provider=mapLayer.dataProvider().name())
elif isinstance(mapLayer, QgsRasterLayer):
li = MapLayerInfo(mapLayer, isVisible=isVisible, provider=mapLayer.dataProvider().name())
elif isinstance(mapLayer, str):
li = MapLayerInfo(mapLayer, isVisible=isVisible, provider=provider)

Benjamin Jakimow
committed
if isinstance(li, MapLayerInfo):
self.mLayerInfos.insert(i, li)
i += 1
def removeLayerInfo(self, mapLayer):
self.removeLayerInfos([mapLayer])
def removeLayerInfos(self, mapLayers):
toRemove = []
sourcesToRemove = []
for ml in mapLayers:
if isinstance(ml, QgsMapLayer):
sourcesToRemove.append(ml.source())
else:
sourcesToRemove.append(ml)
toRemove = [li for li in self.mLayerInfos if li in sourcesToRemove]
for li in toRemove:
self.mLayerInfos.remove(li)
def layerSources(self):
return [li.mSrc for li in self.mLayerInfos]
def layers(self):
return [li.layer() for li in self.mLayerInfos]
def visibleLayers(self):
return [li.layer() for li in self.mLayerInfos if li.isVisible()]
def rowCount(self, QModelIndex_parent=None, *args, **kwargs):
return len(self.mLayers)
def columnCount(self, QModelIndex_parent=None, *args, **kwargs):
return self
class MapCanvas(QgsMapCanvas):
saveFileDirectories = dict()
sigShowProfiles = pyqtSignal(SpatialPoint, str)
sigSpatialExtentChanged = pyqtSignal(SpatialExtent)
sigChangeDVRequest = pyqtSignal(QgsMapCanvas, str)
sigChangeMVRequest = pyqtSignal(QgsMapCanvas, str)
sigChangeSVRequest = pyqtSignal(QgsMapCanvas, QgsRasterRenderer)

benjamin.jakimow@geo.hu-berlin.de
committed
sigDataLoadingFinished = pyqtSignal(np.timedelta64)

Benjamin Jakimow
committed
sigCrosshairPositionChanged = pyqtSignal(SpatialPoint)
sigCrosshairVisibilityChanged = pyqtSignal(bool)
from .crosshair import CrosshairStyle
sigCrosshairStyleChanged = pyqtSignal(CrosshairStyle)
def __init__(self, parent=None):
super(MapCanvas, self).__init__(parent=parent)
self.mLayerModel = MapCanvasLayerModel(parent=self)
#the canvas
self.mRefreshScheduled = False

benjamin.jakimow@geo.hu-berlin.de
committed
def resetRenderStartTime():
self.mRenderStartTime = np.datetime64('now' ,'ms')
resetRenderStartTime()
def emitRenderTimeDelta(*args):
dt = np.datetime64('now', 'ms') - self.mRenderStartTime
self.sigDataLoadingFinished.emit(dt)
self.renderStarting.connect(resetRenderStartTime)
self.renderComplete.connect(emitRenderTimeDelta)
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)
self.setCanvasColor(SETTINGS.value('CANVAS_BACKGROUND_COLOR', QColor(0, 0, 0)))
self.setContextMenuPolicy(Qt.DefaultContextMenu)
#refreshTimer.timeout.connect(self.onTimerRefresh)
#self.extentsChanged.connect(lambda : self._setDataRefreshed())
self.extentsChanged.connect(lambda : self.sigSpatialExtentChanged.emit(self.spatialExtent()))
#self.mLazyRasterSources = []
#self.mLazyVectorSources = []
self.mRendererRaster = None
self.mRendererVector = None
self.mWasVisible = False
self.mMapSummary = self.mapSummary()
from timeseriesviewer.crosshair import CrosshairMapCanvasItem

Benjamin Jakimow
committed
self.mCrosshairItem = CrosshairMapCanvasItem(self)
self.mMapTools = dict()
self.mMapTools['zoomOut'] = QgsMapToolZoom(self, True)
self.mMapTools['zoomIn'] = QgsMapToolZoom(self, False)
self.mMapTools['pan'] = QgsMapToolPan(self)
from timeseriesviewer.maptools import CursorLocationMapTool
mt = CursorLocationMapTool(self)
mt.sigLocationRequest.connect(lambda c: self.sigShowProfiles.emit(c, 'identifyTemporalProfile'))
self.mMapTools['identifyTemporalProfile'] = mt
mt = CursorLocationMapTool(self)
mt.sigLocationRequest.connect(lambda c : self.sigShowProfiles.emit(c, 'identifySpectralProfile'))
self.mMapTools['identifySpectralProfile'] = mt
mt = CursorLocationMapTool(self)
#mt.sigLocationRequest.connect(self.sigShowProfiles.emit)
mt.sigLocationRequest.connect(lambda c: self.sigShowProfiles.emit(c, 'identifyCursorLocationValues'))
self.mMapTools['identifyCursorLocationValues'] = mt
mt = CursorLocationMapTool(self)
mt.sigLocationRequest.connect(lambda pt: self.setCenter(pt))
self.mMapTools['moveCenter'] = mt

Benjamin Jakimow
committed
def mousePressEvent(self, event:QMouseEvent):
b = event.button() == Qt.LeftButton
if b and isinstance(self.mapTool(), QgsMapTool):
from timeseriesviewer.maptools import CursorLocationMapTool
b = isinstance(self.mapTool(), (QgsMapToolIdentify,
CursorLocationMapTool,
SpectralProfileMapTool, TemporalProfileMapTool))
super(MapCanvas, self).mousePressEvent(event)
if b:
ms = self.mapSettings()
pointXY = ms.mapToPixel().toMapCoordinates(event.x(), event.y())
spatialPoint = SpatialPoint(ms.destinationCrs(), pointXY)
self.setCrosshairPosition(spatialPoint)
def setMapView(self, mapView):
from timeseriesviewer.mapvisualization import MapView
assert isinstance(mapView, MapView)
self.mMapView = mapView

Benjamin Jakimow
committed
scope = self.expressionContextScope()
def setTSD(self, tsd):
from timeseriesviewer.timeseries import TimeSeriesDatum
assert isinstance(tsd, TimeSeriesDatum)
self.mTSD = tsd

Benjamin Jakimow
committed
scope = self.expressionContextScope()
scope.setVariable('map_date', str(tsd.date), isStatic=True)
scope.setVariable('map_doy', tsd.doy, isStatic=True)
scope.setVariable('map_sensor', tsd.sensor.name(), isStatic=False)
tsd.sensor.sigNameChanged.connect(lambda name : scope.setVariable('map_sensor', name))
s = ""
def setFixedSize(self, size):
assert isinstance(size, QSize)
if self.size() != size:
super(MapCanvas, self).setFixedSize(size)
def setCrs(self, crs):
assert isinstance(crs, QgsCoordinateReferenceSystem)
if self.crs() != crs:
self.setDestinationCrs(crs)
return self.mapSettings().destinationCrs()
def onVectorOverlayChange(self, *args):
self.refresh()
"""
def depr_mapLayersToRender(self, *args):
if len(self.mLazyRasterSources) > 0:
mls = [QgsRasterLayer(src) for src in self.mLazyRasterSources]
QgsProject.instance().addMapLayers(mls, False)
self.mLayerModel.extend(mls)

benjamin.jakimow@geo.hu-berlin.de
committed
self.setRenderer(self.mRendererRaster, refresh=False)
if len(self.mLazyVectorSources) > 0:
for t in self.mLazyVectorSources:
lyr, path, name, provider = t
#lyr = QgsVectorLayer(path, name, provider, False)
#lyr = t
#add vector layers on top
lyr.rendererChanged.connect(self.onVectorOverlayChange)
self.mLayerModel.insert(0, lyr)
del self.mLazyVectorSources[:]
self.setRenderer(self.mRendererVector, refresh=False)

benjamin.jakimow@geo.hu-berlin.de
committed
return self.mLayerModel
"""
#def setLazyRasterSources(self, sources):
# del self.mLazyRasterSources[:]
# assert isinstance(sources, list)
# self.mLazyRasterSources.extend(sources[:])
#def setLazyVectorSources(self, sourceLayers):
# assert isinstance(sourceLayers, list)
# del self.mLazyVectorSources[:]
# for lyr in sourceLayers:
# assert isinstance(lyr, QgsVectorLayer)
# self.mLazyVectorSources.append((lyr, lyr.source(), lyr.name(), lyr.providerType()))
def mapSummary(self):
dom = QDomDocument()
root = dom.createElement('renderer')
dom.appendChild(root)
if self.mRendererVector:
self.mRendererVector.writeXML(dom, root)
if self.mRendererRaster:
self.mRendererRaster.writeXML(dom, root)
xml = dom.toString()
return (self.crs(), self.spatialExtent(), self.size(), self.mLayerModel.visibleLayers(), xml)
def setLayerSet(self, *args):
raise DeprecationWarning()
def layerModel(self):
return self.mLayerModel

benjamin.jakimow@geo.hu-berlin.de
committed
def setLayers(self, mapLayers):
"""
Set the map layers and, if necessary, registers the in a QgsMapLayerStore
:param mapLayers:
"""

benjamin.jakimow@geo.hu-berlin.de
committed
from .main import TimeSeriesViewer
tsv = TimeSeriesViewer.instance()
if isinstance(tsv, TimeSeriesViewer):
store = tsv.mapLayerStore()
else:
store = QgsProject.instance()
store.addMapLayers(mapLayers)
super(MapCanvas, self).setLayers(mapLayers)
#self.refresh()
#low-level, only performed if MapCanvas is visible
isVisible = self.visibleRegion().boundingRect().isValid() and self.isVisible()
if not isVisible:
self.mRefreshScheduled = True
else:
mLyrs = self.layers()
vLyrs = self.mLayerModel.visibleLayers()
if mLyrs != vLyrs:
self.setLayers(vLyrs)
if self.renderFlag() or force:
super(MapCanvas, self).refresh()
##self.checkRenderFlag()
#if self.renderFlag() or force:
# self.setLayers(self.mLayerModel.visibleLayers())
# super(MapCanvas, self).refresh()

Benjamin Jakimow
committed
def setCrosshairStyle(self, crosshairStyle, emitSignal=True):
from timeseriesviewer.crosshair import CrosshairStyle
if crosshairStyle is None:

Benjamin Jakimow
committed
self.mCrosshairItem.crosshairStyle.setShow(False)
self.mCrosshairItem.update()
else:
assert isinstance(crosshairStyle, CrosshairStyle)

Benjamin Jakimow
committed
self.mCrosshairItem.setCrosshairStyle(crosshairStyle)

Benjamin Jakimow
committed
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
if emitSignal:
self.sigCrosshairStyleChanged.emit(self.mCrosshairItem.crosshairStyle)
def crosshairStyle(self)->CrosshairStyle:
return self.mCrosshairItem.crosshairStyle
def setCrosshairPosition(self, spatialPoint:SpatialPoint, emitSignal=True):
"""
Sets the position of the Crosshair.
:param spatialPoint: SpatialPoint
:param emitSignal: True (default). Set False to avoid emitting sigCrosshairPositionChanged
:return:
"""
point = spatialPoint.toCrs(self.mapSettings().destinationCrs())
self.mCrosshairItem.setPosition(point)
if emitSignal:
self.sigCrosshairPositionChanged.emit(point)
def crosshairPosition(self)->SpatialPoint:
"""Returns the last crosshair position"""
return self.mCrosshairItem.mPosition
def setCrosshairVisibility(self, b:bool, emitSignal=True):
"""
Sets the Crosshair visbility
:param b: bool
"""
if b and self.mCrosshairItem.mPosition is None:
self.mCrosshairItem.setPosition(self.spatialCenter())
self.sigCrosshairPositionChanged.emit(self.spatialCenter())
if b != self.mCrosshairItem.visibility():
self.mCrosshairItem.setVisibility(b)
if emitSignal:
self.sigCrosshairVisibilityChanged.emit(b)
def checkRenderFlag(self):
"""
Controls the MapCanvas Render flag to decide if rendering is required
:return:
"""
wasVisible = self.mWasVisible
isVisible = self.visibleRegion().boundingRect().isValid() \
if not isVisible:
self.setRenderFlag(False)
self.mWasVisible = False
#will stop active render jobs
#the canvas is visible, but is a new rendering required?
lastSummary = self.mMapSummary
self.mMapSummary = self.mapSummary()
#isRequired = (wasVisible == False) or self.renderFlag() or self.mDataRefreshed
#print(lastSummary)
#print(self.mMapSummary)
isRequired = lastSummary != self.mapSummary()
self.mWasVisible = True
if isRequired:
self.setRenderFlag(True)
#self.mMapSummary = self.mapSummary()

benjamin.jakimow@geo.hu-berlin.de
committed
def layerPaths(self):
return [str(l.source()) for l in self.layers()]
def pixmap(self):
"""
Returns the current map image as pixmap
pixmap = QPixmap(self.rect().size())
painter = QPainter(pixmap)
self.render(painter)
return pixmap
def contextMenuEvent(self, event):
menu = QMenu()
# add general options
menu.addSeparator()
m = menu.addMenu('Stretch to current extent...')
action = m.addAction('Linear')
action.triggered.connect(lambda : self.stretchToExtent(self.spatialExtent(), 'linear_minmax', p=0.0))
action = m.addAction('Linear 5%')
action.triggered.connect(lambda: self.stretchToExtent(self.spatialExtent(), 'linear_minmax', p=0.05))
action = m.addAction('Gaussian')
action.triggered.connect(lambda: self.stretchToExtent(self.spatialExtent(), 'gaussian', n=3))
action = menu.addAction('Zoom to Layer')
action.triggered.connect(lambda : self.setSpatialExtent(self.spatialExtentHint()))
action = menu.addAction('Refresh')
action.triggered.connect(lambda: self.refresh(True))
menu.addSeparator()
action = menu.addAction('Change crosshair style')

Benjamin Jakimow
committed
def onCrosshairChange(*args):
style = CrosshairDialog.getCrosshairStyle(parent=self,

benjamin.jakimow@geo.hu-berlin.de
committed
mapCanvas=self,

Benjamin Jakimow
committed
crosshairStyle=self.mCrosshairItem.crosshairStyle)
if isinstance(style, CrosshairStyle):
self.setCrosshairStyle(style)
action.triggered.connect(onCrosshairChange)

Benjamin Jakimow
committed
if self.mCrosshairItem.visibility():
action = menu.addAction('Hide crosshair')

Benjamin Jakimow
committed
action.triggered.connect(lambda : self.setCrosshairVisibility(False))
else:
action = menu.addAction('Show crosshair')

Benjamin Jakimow
committed
action.triggered.connect(lambda: self.setCrosshairVisibility(True))
menu.addSeparator()
m = menu.addMenu('Copy...')
action = m.addAction('Date')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_date'))
action = m.addAction('Sensor')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_sensor'))
action = m.addAction('Path')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'copy_path'))

benjamin.jakimow@geo.hu-berlin.de
committed
action = m.addAction('Map')
action.triggered.connect(lambda: QApplication.clipboard().setPixmap(self.pixmap()))
m = menu.addMenu('Map Coordinates...')
ext = self.spatialExtent()
center = self.spatialExtent().spatialCenter()
action = m.addAction('Extent (WKT Coordinates)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktCoordinates()))
action = m.addAction('Extent (WKT Polygon)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.asWktPolygon()))
m.addSeparator()
action = m.addAction('Map Center (WKT)')

benjamin.jakimow@geo.hu-berlin.de
committed
action.triggered.connect(lambda: QApplication.clipboard().setText(center.wellKnownText()))

benjamin.jakimow@geo.hu-berlin.de
committed
action.triggered.connect(lambda: QApplication.clipboard().setText(center.toString()))
action = m.addAction('Map Extent (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.wellKnownText()))
action = m.addAction('Map Extent')
action.triggered.connect(lambda: QApplication.clipboard().setText(ext.toString()))

benjamin.jakimow@geo.hu-berlin.de
committed
m.addSeparator()
action = m.addAction('CRS (EPSG)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().authid()))
action = m.addAction('CRS (WKT)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toWkt()))
action = m.addAction('CRS (Proj4)')
action.triggered.connect(lambda: QApplication.clipboard().setText(self.crs().toProj4()))
action = m.addAction('PNG')
action.triggered.connect(lambda : self.saveMapImageDialog('PNG'))
action = m.addAction('JPEG')
action.triggered.connect(lambda: self.saveMapImageDialog('JPG'))

benjamin.jakimow@geo.hu-berlin.de
committed
from timeseriesviewer.utils import qgisInstance
actionAddRaster2QGIS = menu.addAction('Add raster layers(s) to QGIS')
actionAddRaster2QGIS.triggered.connect(lambda : self.addLayers2QGIS(
[l for l in self.layers() if isinstance(l, QgsRasterLayer)]
)
)
# QGIS 3: action.triggered.connect(lambda: QgsProject.instance().addMapLayers([l for l in self.layers() if isinstance(l, QgsRasterLayer)]))
actionAddVector2QGIS = menu.addAction('Add vector layer(s) to QGIS')
actionAddRaster2QGIS.triggered.connect(lambda : self.addLayers2QGIS(
#QgsProject.instance().addMapLayers(
[l for l in self.layers() if isinstance(l, QgsVectorLayer)]
)
)
# QGIS 3: action.triggered.connect(lambda: QgsProject.instance().addMapLayers([l for l in self.layers() if isinstance(l, QgsVectorLayer)]))
b = isinstance(qgisInstance(), QgisInterface)
for a in [actionAddRaster2QGIS, actionAddVector2QGIS]:
a.setEnabled(b)
menu.addSeparator()
action = menu.addAction('Hide date')
action.triggered.connect(lambda : self.sigChangeDVRequest.emit(self, 'hide_date'))
action = menu.addAction('Remove date')
action.triggered.connect(lambda: self.sigChangeDVRequest.emit(self, 'remove_date'))
action = menu.addAction('Hide map view')
action.triggered.connect(lambda: self.sigChangeMVRequest.emit(self, 'hide_mapview'))
action = menu.addAction('Remove map view')
action.triggered.connect(lambda: self.sigChangeMVRequest.emit(self, 'remove_mapview'))
menu.exec_(event.globalPos())
def addLayers2QGIS(self, mapLayers):
from timeseriesviewer.utils import qgisInstance
iface = qgisInstance()
if isinstance(iface, QgisInterface):
grpNode= iface.layerTreeView().currentGroupNode()
assert isinstance(grpNode, QgsLayerTreeGroup)
for l in mapLayers:
if isinstance(l, QgsRasterLayer):
lqgis = iface.addRasterLayer(l.source(), l.name())
lqgis.setRenderer(l.renderer().clone())
lqgis = iface.addVectorLayer(l.source(), l.name(), 'ogr')
def stretchToCurrentExtent(self):
se = self.spatialExtent()
self.stretchToExtent(se)
def stretchToExtent(self, spatialExtent, stretchType='linear_minmax', **stretchArgs):
"""
:param spatialExtent: rectangle to get the image statistics for
:param stretchType: ['linear_minmax' (default), 'gaussian']
:param stretchArgs:
linear_minmax: 'p' percentage from min/max, e.g. +- 5 %
gaussian: 'n' mean +- n* standard deviations
:return:
"""
for l in self.layers():
if isinstance(l, QgsRasterLayer):
r = l.renderer()
dp = l.dataProvider()
newRenderer = None
assert isinstance(dp, QgsRasterDataProvider)
def getCE(band):
stats = dp.bandStatistics(band, QgsRasterBandStats.All, extent, 500)
# hist = dp.histogram(band,100, stats.minimumValue, stats.maximumValue, extent, 500, False)
ce = QgsContrastEnhancement(dp.dataType(band))
d = (stats.maximumValue - stats.minimumValue)
if stretchType == 'linear_minmax':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.minimumValue + d * stretchArgs.get('p', 0))
ce.setMaximumValue(stats.maximumValue - d * stretchArgs.get('p', 0))
elif stretchType == 'gaussian':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.mean - stats.stdDev * stretchArgs.get('n', 3))
ce.setMaximumValue(stats.mean + stats.stdDev * stretchArgs.get('n', 3))
else:
# stretchType == 'linear_minmax':
ce.setContrastEnhancementAlgorithm(QgsContrastEnhancement.StretchToMinimumMaximum)
ce.setMinimumValue(stats.minimumValue)
ce.setMaximumValue(stats.maximumValue)
return ce
if isinstance(r, QgsMultiBandColorRenderer):
#newRenderer = QgsMultiBandColorRenderer(None, r.redBand(), r.greenBand(), r.blueBand())
newRenderer = cloneRenderer(r)
ceR = getCE(r.redBand())
ceG = getCE(r.greenBand())
ceB = getCE(r.blueBand())
if False:
vMin = min(ceR.minimumValue(), ceG.minimumValue(), ceB.minimumValue())
vMax = max(ceR.maximumValue(), ceG.maximumValue(), ceB.maximumValue())
for ce in [ceR, ceG, ceB]:
assert isinstance(ce, QgsContrastEnhancement)
ce.setMaximumValue(vMax)
ce.setMinimumValue(vMin)
newRenderer.setRedContrastEnhancement(ceR)
newRenderer.setGreenContrastEnhancement(ceG)
newRenderer.setBlueContrastEnhancement(ceB)
elif isinstance(r, QgsSingleBandPseudoColorRenderer):
ce = getCE(newRenderer.band())
#stats = dp.bandStatistics(newRenderer.band(), QgsRasterBandStats.All, extent, 500)
shader = newRenderer.shader()
newRenderer.setClassificationMax(ce.maximumValue())
newRenderer.setClassificationMin(ce.minimumValue())
shader.setMaximumValue(ce.maximumValue())
shader.setMinimumValue(ce.minimumValue())
elif isinstance(r, QgsSingleBandGrayRenderer):
newRenderer = cloneRenderer(r)
s = ""
elif isinstance(r, QgsPalettedRasterRenderer):
s = ""
if newRenderer is not None:
self.sigChangeSVRequest.emit(self, newRenderer)
s = ""
def activateMapTool(self, key):
if key is None:
self.setMapTool(None)
elif key in self.mMapTools.keys():
super(MapCanvas, self).setMapTool(self.mMapTools[key])
else:
s = ""
#logger.error('unknown map tool key "{}"'.format(key))
def saveMapImageDialog(self, fileType):
lastDir = SETTINGS.value('CANVAS_SAVE_IMG_DIR', os.path.expanduser('~'))
from timeseriesviewer.utils import saveFilePath
from timeseriesviewer.mapvisualization import MapView
if isinstance(self.mTSD, TimeSeriesDatum) and isinstance(self.mMapView, MapView):
path = saveFilePath('{}.{}'.format(self.mTSD.date, self.mMapView.title()))
else:
path = 'mapcanvas'
path = jp(lastDir, '{}.{}'.format(path, fileType.lower()))
path, _ = QFileDialog.getSaveFileName(self, 'Save map as {}'.format(fileType), path)
if len(path) > 0:
self.saveAsImage(path, None, fileType)
SETTINGS.setValue('CANVAS_SAVE_IMG_DIR', os.path.dirname(path))

benjamin.jakimow@geo.hu-berlin.de
committed
def setRenderer(self, renderer, refresh=True):
success = self.layerModel().setRenderer(renderer)
self.setRenderFlag(True)
#self.refresh()
def setSpatialExtent(self, spatialExtent):
assert isinstance(spatialExtent, SpatialExtent)
if self.spatialExtent() != spatialExtent:
b = self.crs() != spatialExtent.crs()
spatialExtent = spatialExtent.toCrs(self.crs())
ext = QgsRectangle(spatialExtent)
if b:
s = ""
self.setCenter(ext.center())
self.setExtent(ext)
s = ""

Benjamin Jakimow
committed
def spatialExtent(self)->SpatialExtent:
"""
Returns the map extent as SpatialExtent (extent + CRS)
:return: SpatialExtent
"""
return SpatialExtent.fromMapCanvas(self)

Benjamin Jakimow
committed
def spatialCenter(self)->SpatialPoint:
"""
Returns the map center as SpatialPoint (QgsPointXY + CRS)
:return: SpatialPoint
"""
return SpatialPoint.fromMapCanvasCenter(self)
def spatialExtentHint(self):
crs = self.crs()
ext = SpatialExtent.world()
for lyr in self.mLayerModel.layers()+ self.layers():
ext = SpatialExtent.fromLayer(lyr).toCrs(crs)
break
return ext
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
class CanvasBoundingBoxItem(QgsGeometryRubberBand):
def __init__(self, mapCanvas):
assert isinstance(mapCanvas, QgsMapCanvas)
super(CanvasBoundingBoxItem, self).__init__(mapCanvas)
self.canvas = mapCanvas
self.mCanvasExtents = dict()
self.mShow = True
self.mShowTitles = True
self.setIconType(QgsGeometryRubberBand.ICON_NONE)
def connectCanvas(self, canvas):
assert isinstance(canvas, QgsMapCanvas)
assert canvas != self.canvas
if canvas not in self.mCanvasExtents.keys():
self.mCanvasExtents[canvas] = None
canvas.extentsChanged.connect(lambda : self.onExtentsChanged(canvas))
canvas.destroyed.connect(lambda : self.disconnectCanvas(canvas))
self.onExtentsChanged(canvas)
def disconnectCanvas(self, canvas):
self.mCanvasExtents.pop(canvas)
def onExtentsChanged(self, canvas):
assert isinstance(canvas, QgsMapCanvas)
ext = SpatialExtent.fromMapCanvas(canvas)
ext = ext.toCrs(self.canvas.mapSettings().destinationCrs())
assert geom.fromWkt(ext.asWktPolygon())
self.mCanvasExtents[canvas] = (ext, geom)
self.refreshExtents()
def refreshExtents(self):
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
if self.mShow:
for canvas, t in self.mCanvasExtents.items():
ext, geom = t
multi.addGeometry(geom.clone())
self.setGeometry(multi)
def paint(self, painter, QStyleOptionGraphicsItem=None, QWidget_widget=None):
super(CanvasBoundingBoxItem, self).paint(painter)
if self.mShowTitles and self.mShow:
painter.setPen(Qt.blue);
painter.setFont(QFont("Arial", 30))
for canvas, t in self.mCanvasExtents.items():
ext, geom = t
ULpx = self.toCanvasCoordinates(ext.center())
txt = canvas.windowTitle()
painter.drawLine(0, 0, 200, 200);
painter.drawText(ULpx, txt)
def setShow(self, b):
assert isinstance(b, bool)
self.mShow = b
def setShowTitles(self, b):
assert isinstance(b, bool)
self.mShowTitles = b
def exampleSyncedCanvases():
global btnCrs, mapCanvases, lyrs, syncExtents
import site, sys
# add site-packages to sys.path as done by enmapboxplugin.py
from timeseriesviewer import sandbox
from timeseriesviewer.utils import SpatialExtent
import example.Images
qgsApp = sandbox.initQgisEnvironment()
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
w = QWidget()
hl1 = QHBoxLayout()
hl2 = QHBoxLayout()
btnCrs = QgsProjectionSelectionWidget(w)
btnRefresh = QPushButton('Refresh', w)
hl1.addWidget(btnCrs)
hl1.addWidget(btnRefresh)
vl = QVBoxLayout()
vl.addLayout(hl1)
vl.addLayout(hl2)
w.setLayout(vl)
files = [example.Images.Img_2014_01_15_LC82270652014015LGN00_BOA,
example.Images.Img_2013_05_20_LC82270652013140LGN01_BOA,
example.Images.Img_2013_08_16_LE72270652013228CUB00_BOA]
mapCanvases = []
lyrs = []
def onRefresh(*args):
crs = btnCrs.crs()
ext = SpatialExtent.fromLayer(lyrs[0]).toCrs(crs)
for mapCanvas in mapCanvases:
mapCanvas.setCrs(crs)
mapCanvas.setSpatialExtent(ext)
mapCanvas.refresh()
mapCanvas.refreshAllLayers()
def syncExtents(ext):
for mapCanvas in mapCanvases:
oldext = SpatialExtent.fromMapCanvas(mapCanvas)
if oldext != ext:
mapCanvas.blockSignals(True)
#mapCanvas.setExtent(ext)
mapCanvas.setSpatialExtent(ext)
mapCanvas.blockSignals(False)
mapCanvas.refreshAllLayers()
def registerMapCanvas(mapCanvas):
mapCanvas.extentsChanged.connect(lambda: syncExtents(SpatialExtent.fromMapCanvas(mapCanvas)))
for i, f in enumerate(files):
ml = QgsRasterLayer(f)
#QgsProject.instance().addMapLayer(ml)
lyrs.append(ml)
#mapCanvas = QgsMapCanvas(w)
mapCanvas = MapCanvas(w)
registerMapCanvas(mapCanvas)
hl2.addWidget(mapCanvas)
#mapCanvas.setLayers([QgsMapCanvasLayer(ml)])
mapCanvas.setLayers([ml])
if i == 0:
btnCrs.setCrs(ml.crs())
mapCanvases.append(mapCanvas)
btnCrs.crsChanged.connect(onRefresh)
btnRefresh.clicked.connect(onRefresh)
w.show()
onRefresh()
qgsApp.exec_()
qgsApp.exitQgis()