Estou tentando escrever um script que salvará uma renderização de várias camadas usando o compositor de mapas. O problema que encontro é que o script salva antes que o qgis termine de renderizar todas as camadas.
Com base em várias outras respostas ( 1 , 2 , 3 ), tentei usar iface.mapCanvas.mapCanvasRefreshed.connect()
e colocar a imagem salva dentro de uma função, mas ainda encontro o mesmo problema - as imagens não incluem todas as camadas.
O código que estou usando, bem como as imagens da aparência da janela principal e das renderizações, estão listadas abaixo.
Percebi que, se a janela do console estiver aberta e descomente as três print layerList
linhas, o programa aguardará a conclusão da renderização antes de salvar as imagens. Não tenho certeza se isso se deve ao aumento do tempo de processamento ou se está mudando a maneira como o programa é executado.
Como implemento isso corretamente para que todas as camadas sejam incluídas na imagem?
from qgis.core import *
from qgis.utils import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os.path
##StackExchange Version=name
##Map_Save_Folder=folder
##Map_Save_Name=string roadmap
# Create save file location
mapName = "%s.png" %Map_Save_Name
outfile = os.path.join(Map_Save_Folder,mapName)
pdfName = "%s.pdf" %Map_Save_Name
outPDF = os.path.join(Map_Save_Folder,pdfName)
# Create point and line layers for later
URIstrP = "Point?crs=EPSG:3035"
layerP = QgsVectorLayer(URIstrP,"pointsPath","memory")
provP = layerP.dataProvider()
URIstrL = "LineString?crs=EPSG:3035"
layerL = QgsVectorLayer(URIstrL,"linePath","memory")
provL = layerL.dataProvider()
# Add points to point layer
feat1 = QgsFeature()
feat2 = QgsFeature()
feat3 = QgsFeature()
feat1.setGeometry(QgsGeometry.fromPoint(QgsPoint(5200000,2600000)))
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(5300000,2800000)))
provP.addFeatures([feat1, feat2])
# Add line to line layer
feat3.setGeometry(QgsGeometry.fromPolyline([feat1.geometry().asPoint(),feat2.geometry().asPoint()]))
provL.addFeatures([feat3])
# Set symbology for line layer
symReg = QgsSymbolLayerV2Registry.instance()
metaRegL = symReg.symbolLayerMetadata("SimpleLine")
symLayL = QgsSymbolV2.defaultSymbol(layerL.geometryType())
metaL = metaRegL.createSymbolLayer({'width':'1','color':'0,0,0'})
symLayL.deleteSymbolLayer(0)
symLayL.appendSymbolLayer(metaL)
symRendL = QgsSingleSymbolRendererV2(symLayL)
layerL.setRendererV2(symRendL)
# Set symbology for point layer
metaRegP = symReg.symbolLayerMetadata("SimpleMarker")
symLayP = QgsSymbolV2.defaultSymbol(layerP.geometryType())
metaP = metaRegP.createSymbolLayer({'size':'3','color':'0,0,0'})
symLayP.deleteSymbolLayer(0)
symLayP.appendSymbolLayer(metaP)
symRendP = QgsSingleSymbolRendererV2(symLayP)
layerP.setRendererV2(symRendP)
# Load the layers
QgsMapLayerRegistry.instance().addMapLayer(layerP)
QgsMapLayerRegistry.instance().addMapLayer(layerL)
iface.mapCanvas().refresh()
# --------------------- Using Map Composer -----------------
def custFunc():
mapComp.exportAsPDF(outPDF)
mapImage.save(outfile,"png")
mapCanv.mapCanvasRefreshed.disconnect(custFunc)
return
layerList = []
for layer in QgsMapLayerRegistry.instance().mapLayers().values():
layerList.append(layer.id())
#print layerList
#print layerList
#print layerList
mapCanv = iface.mapCanvas()
bound = layerP.extent()
bound.scale(1.25)
mapCanv.setExtent(bound)
mapRend = mapCanv.mapRenderer()
mapComp = QgsComposition(mapRend)
mapComp.setPaperSize(250,250)
mapComp.setPlotStyle(QgsComposition.Print)
x, y = 0, 0
w, h = mapComp.paperWidth(), mapComp.paperHeight()
composerMap = QgsComposerMap(mapComp, x, y, w, h)
composerMap.zoomToExtent(bound)
mapComp.addItem(composerMap)
#mapComp.exportAsPDF(outPDF)
mapRend.setLayerSet(layerList)
mapRend.setExtent(bound)
dpmm = dpmm = mapComp.printResolution() / 25.4
mapImage = QImage(QSize(int(dpmm*w),int(dpmm*h)), QImage.Format_ARGB32)
mapImage.setDotsPerMeterX(dpmm * 1000)
mapImage.setDotsPerMeterY(dpmm * 1000)
mapPaint = QPainter()
mapPaint.begin(mapImage)
mapRend.render(mapPaint)
mapComp.renderPage(mapPaint,0)
mapPaint.end()
mapCanv.mapCanvasRefreshed.connect(custFunc)
#mapImage.save(outfile,"png")
Como é a aparência na janela principal do QGIS (existe um mapa raster aleatório no qual está sendo exibido):
Como informações adicionais, estou usando o QGIS 2.18.7 no Windows 7
mapCanv.mapCanvasRefreshed.connect(custFunc)
pormapCanv.renderComplete.connect(custFunc)
?layerP .commitChanges()
) Embora eu não entenda por que isso deve ajudar, pois você está salvando apenas a imagem, mas vale a pena tentar, eu acho. Caso contrário, espero que outros possam aconselhar :)commitChanges()
, mas sem sorte, infelizmente. Obrigado pela sugestão.Respostas:
Existem diferentes problemas surgindo aqui
Renderização na tela versus renderização em uma imagem
O sinal
mapCanvasRefreshed
é emitido repetidamente enquanto a tela está sendo renderizada para a tela. Para exibição na tela, isso fornece um feedback mais rápido, o que pode ser bom para um usuário ver algo acontecendo ou ajudar na navegação.Para renderizações fora da tela, como salvar em um arquivo, isso não é confiável (pois você só terá uma imagem completa se a renderização for rápida o suficiente).
O que pode ser feito: não precisamos da tela do mapa para renderizar sua imagem. Podemos apenas copiar o
QgsMapSettings
da tela do mapa. Essas configurações são os parâmetros enviados ao renderizador e definem o que exatamente e como exatamente as coisas devem ser convertidas de todos os provedores de dados em uma imagem rasterizada.Registro de camada vs tela de mapa
As camadas adicionadas ao registro não acabam na tela imediatamente, mas apenas na próxima execução do loop de eventos. Portanto, é melhor fazer uma das duas coisas a seguir
Inicie a renderização da imagem em um cronômetro.
QTimer.singleShot(10, render_image)
ExecuteIsso funciona, mas é uma chamada perigosa de usar (às vezes leva a falhas estranhas) e, portanto, deve ser evitada.QApplication.processEvents()
após adicionar a camada.Mostre-me o código
O código a seguir faz isso (ligeiramente ajustado no QFieldSync , consulte se você estiver interessado em mais personalização)
fonte
renderComplete
sinal não está funcionando?painter
emitido com ele, no qual você ainda pode desenhar coisas adicionais que acabarão na imagem final (e a partir da qual você provavelmente também poderá obter a imagem final para fazer essa abordagem funcionar).QTimer.singleShot(10, render_image)