Iterating over layers and exporting them as PNG images with PyQGIS in standalone script
An answer to this question on the GIS Stack Exchange.
Question
How do I export specific layers as a png from a QGIS .qgs map?
From the table of contents the script would first export the "boundary, climits, and Div1_Irrig_1956_0" layers as one png. The standalone script would then iterate to export "boundary, climits, and Div1_Irrig_1976_1" layers as the next png and so on. I am working with the script below to begin with but am only getting one png with all of the layers exported.
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import *
from qgis.utils import iface
import sys, os
import glob
qgs = QgsApplication(sys.argv, True)
qgis_prefix = "C:\\OSGeo4W\\apps\\qgis"
QgsApplication.setPrefixPath(qgis_prefix, True)
qgs.initQgis()
#layers = glob.glob((configData['destination_folder'])+"\\*.shp")
layers = glob.glob(r"E:\IrrigatedLands\FC_qgis\*.shp")
for layer in layers:
print layer
irrig = QgsVectorLayer(layer, "testlayer_shp", "ogr")
print irrig.isValid()
layerset = []
QgsMapLayerRegistry.instance().addMapLayer(irrig)
layerset.append(irrig.id())
# create image
imageType = "png"
pathToFile = "C:\\Users\\James\\Desktop\\"
name = "render"
img = QImage(QSize(800, 600), QImage.Format_ARGB32_Premultiplied)
# set image's background color
color = QColor(255, 255, 255)
img.fill(color.rgb())
# create painter
p = QPainter()
p.begin(img)
p.setRenderHint(QPainter.Antialiasing)
render = QgsMapRenderer()
# set layer set
layer_set = [irrig.id()] # add ID of every layer
print layer_set
render.setLayerSet(layer_set)
# set extent
rect = QgsRectangle(render.fullExtent())
rect.scale(1.1)
render.setExtent(rect)
# set output size
render.setOutputSize(img.size(), img.logicalDpiX())
# do the rendering
render.render(p)
p.end()
img.save(pathToFile + name + "." + imageType ,imageType)
Answer
I had difficulty getting the composer to work and Germán Carrillo's answer is outdated.
Germán's answer also assumes that the entire layer can be rendered within a fixed time interval that is the same for each layer. In my use case, this couldn't be done reliably. Therefore, I used the following code:
prefix="probe_"
layers = []
for layer in QgsProject.instance().mapLayers().values():
if layer.name().startswith("probe_"):
layers.append(layer)
p = QgsProject().instance()
layout = QgsPrintLayout(p)
layout.initializeDefaults()
map = QgsLayoutItemMap(layout)
map.setRect(20, 20, 20, 20)
print(layers[0].extent())
map.setExtent(layers[0].extent())
layout.addLayoutItem(map)
map.attemptMove(QgsLayoutPoint(0, 0, QgsUnitTypes.LayoutInches))
map.attemptResize(QgsLayoutSize(8.5, 11, QgsUnitTypes.LayoutInches))
exporter = QgsLayoutExporter(layout)
settings = QgsLayoutExporter.ImageExportSettings()
settings.dpi = 300
current_layer_idx = 0
def hide_layers(layers):
root = QgsProject.instance().layerTreeRoot()
for layer in layers:
node = root.findLayer(layer.id())
node.setItemVisibilityChecked(Qt.Unchecked)
def show_layer(layer):
root = QgsProject.instance().layerTreeRoot()
node = root.findLayer(layer.id())
node.setItemVisibilityChecked(Qt.Checked)
def prepare():
global layer_name, out_name
hide_layers(layers)
show_layer(layers[current_layer_idx])
layer_name = layers[current_layer_idx].name()
out_name = layer_name + ".png"
def export():
global current_layer_idx
print("Saving ",out_name)
ret = exporter.exportToImage(out_name, settings)
assert ret==0
current_layer_idx += 1
And manually entered prepare() and export() cyclicly as appropriate.
