Skip to content

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.

enter image description here

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.