PyQGIS – Iterating Over Layer’s Features and Rendering PNG for Each Feature

pyqgis

I have a vector layer with about 2500 features, each feature is of a square shape and i need to get each feature as a PNG. I have rendered the PNG for the layer as a whole, but I've been unable to export each individual feature. The code works exactly as it should if there was only one feature in the layer. What would be the most optimum method without having to create individual layers for all my 2500 features.

def finished():
    img = render.renderedImage()
    # save the image; img.save("C:\QGIS Project\Restructured\Rev1\\test_render.png","png")
    img.save(f'C:\QGIS Project\Restructured\Rev1\\test_render_{feature[0]}.png', "png")

layer = QgsProject.instance().mapLayersByName("Rotated")[0]
settings = QgsMapSettings()
settings.setLayers([layer])
settings.setBackgroundColor(QColor(255, 255, 255))
settings.setOutputSize(QSize(400, 400))
features = layer.getFeatures()
settings.setExtent(layer.extent())

for feature in features:
    render = QgsMapRendererParallelJob(settings)
    render.finished.connect(finished)
    # Start the rendering
    render.start()
    render.waitForFinished()

Best Answer

Try moving the setExtent() call on the QgsMapSettings object inside your loop and pass the bounding box of the current feature's geometry on each iteration:

def finished():
    img = render.renderedImage()
    # save the image; img.save("C:\QGIS Project\Restructured\Rev1\\test_render.png","png")
    img.save(f'C:\QGIS Project\Restructured\Rev1\\test_render_{feature[0]}.png', "png")

layer = QgsProject.instance().mapLayersByName("Rotated")[0]
settings = QgsMapSettings()
settings.setLayers([layer])
settings.setBackgroundColor(QColor(255, 255, 255))
settings.setOutputSize(QSize(400, 400))

for feature in layer.getFeatures():
    layer.setSubsetString("fid = {}".format(feature.id()))
    settings.setExtent(feature.geometry().boundingBox())
    render = QgsMapRendererParallelJob(settings)
    render.finished.connect(finished)
    # Start the rendering
    render.start()
    render.waitForFinished()

layer.setSubsetString("")

By the way, if you wish to add a small margin around each feature's extent you can use the grow() method on the QgsRectangle object returned by feature.geometry().boundingBox(). So your loop block could look like:

for feature in layer.getFeatures():
    layer.setSubsetString("fid = {}".format(feature.id()))
    extent = feature.geometry().boundingBox()
    extent.grow(0.01) # Play with the value to find a suitable margin
    settings.setExtent(extent)
    render = QgsMapRendererParallelJob(settings)
    render.finished.connect(finished)
    # Start the rendering
    render.start()
    render.waitForFinished()