QGIS PNG – Saving Map Canvas as PNG with Transparent Background Programmatically with QGIS

pngpyqgisrenderingtransparency

I wrote a script to create a big number of heat maps for a given scenario. I would like to save the result of every heat map as an PNG image. I have figured it out doing it with simple Map Rendering but the quality of the result is too low. Furthermore I would like to export to OpenLayers using the same code, which does not seem to be possible with simple rendering.

Now I am trying to do it with QgsComposition instead. Manually in the Map Composer you can just set the paper color to transparent and disable the background of the map canvas.

This is the code that I have so far. I just cannot figure out why it is not working. The result png-file always has a white opaque background.

My OS is Windows 7 and I'm using QGIS 2.6 Brighton.

I hope someone can help me out here.

# PARAMETERS
mainPath = 'path/to/folder/'
filename = 'filename'
imageType = "png"
imageWidth_mm = 400
imageHeight_mm = 160
dpi = 300

mapRenderer = iface.mapCanvas().mapRenderer()

c = QgsComposition(mapRenderer)
c.setPlotStyle(QgsComposition.Print)

c.setPaperSize(400, 160)

x, y = 0, 0
w, h = c.paperWidth(), c.paperHeight()
composerMap = QgsComposerMap(c, x ,y, w, h)
c.addItem(composerMap)

# get all items and disable the backgrounds
itemList = c.items()
c.removeItem(itemList[2])
c.removeItem(itemList[2])
itemList[2].setBackgroundEnabled(False)
itemList[3].setBackgroundEnabled(False)
# add them to the composition
c.addItem(itemList[2])
c.addItem(itemList[3])

c.refreshItems()
c.refreshDataDefinedProperty(QgsComposerObject.AllProperties)

c.setPrintResolution(dpi)
dpmm = dpi / 25.4
width = int(dpmm * c.paperWidth())
height = int(dpmm * c.paperHeight())

# create output image and initialize it
# image = QImage(QSize(width, height), QImage.Format_ARGB32)
image = QImage(QSize(width, height), QImage.Format_ARGB32_Premultiplied)
image.setDotsPerMeterX(dpmm * 1000)
image.setDotsPerMeterY(dpmm * 1000)
#image.fill(0)

imagePainter = QPainter(image)

# This part does not seem to have any effect
#brush = QBrush(Qt.BrushStyle(0)) # transparent brush
#
#imagePainter.setBackgroundMode(0)
#c.setBackgroundBrush(brush)

c.renderPage( imagePainter, 0 )
imagePainter.end()


imageFilename =  mainPath + filename + '.' + imageType
image.save(imageFilename, imageType)
print 'image saved'
print 'done'

Best Answer

You can set the page background to transparent by creating a transparent symbol and setting the page background to use that symbol:

transparent_fill = QgsFillSymbolV2.createSimple({ 'outline_style': 'no', 'style': 'no'})
c.setPageStyleSymbol( transparent_fill )

There's also a few other things wrong with your script:

First,

mapRenderer = iface.mapCanvas().mapRenderer()
c = QgsComposition(mapRenderer)

mapRenderer is deprecated and will be removed soon. The replacement is QgsMapSettings:

map_settings = iface.mapCanvas().mapSettings()
c = QgsComposition(map_settings)

Next,

# get all items and disable the backgrounds
itemList = c.items()
c.removeItem(itemList[2])
c.removeItem(itemList[2])
itemList[2].setBackgroundEnabled(False)
itemList[3].setBackgroundEnabled(False)
# add them to the composition
c.addItem(itemList[2])
c.addItem(itemList[3])

c.refreshItems()
c.refreshDataDefinedProperty(QgsComposerObject.AllProperties)

Most of this isn't required. Just set the map item to have no background after you've created it:

composerMap = QgsComposerMap(c, x ,y, w, h)
composerMap.setBackgroundEnabled(False)

Lastly:

# image = QImage(QSize(width, height), QImage.Format_ARGB32)
image = QImage(QSize(width, height), QImage.Format_ARGB32_Premultiplied)
image.setDotsPerMeterX(dpmm * 1000)
image.setDotsPerMeterY(dpmm * 1000)
#image.fill(0)

Should be:

image = QImage(QSize(width, height), QImage.Format_ARGB32)
image.setDotsPerMeterX(dpmm * 1000)
image.setDotsPerMeterY(dpmm * 1000)
image.fill( Qt.transparent )

Don't use a premultiplied image, and you MUST initialise the image with a color. In this case we'll initialise it by filling it with a transparent color.

Here's a working (and cleaned up) version:

from PyQt4.QtCore import *
from PyQt4.QtGui import *

# PARAMETERS
mainPath = '/path/to/folder/'
filename = 'filename'
imageType = "png"
imageWidth_mm = 400
imageHeight_mm = 160
dpi = 300

map_settings = iface.mapCanvas().mapSettings()
c = QgsComposition(map_settings)
c.setPaperSize(400, 160)
c.setPrintResolution(dpi)

#set page background to transparent
transparent_fill =QgsFillSymbolV2.createSimple({ 'outline_style': 'no', 'style': 'no'})
c.setPageStyleSymbol( transparent_fill )

x, y = 0, 0
w, h = c.paperWidth(), c.paperHeight()
composerMap = QgsComposerMap(c, x ,y, w, h)
composerMap.setBackgroundEnabled(False)
c.addItem(composerMap)

dpmm = dpi / 25.4
width = int(dpmm * c.paperWidth())
height = int(dpmm * c.paperHeight())

# create output image and initialize it
image = QImage(QSize(width, height), QImage.Format_ARGB32)
image.setDotsPerMeterX(dpmm * 1000)
image.setDotsPerMeterY(dpmm * 1000)
image.fill(Qt.transparent)

imagePainter = QPainter(image)

c.setPlotStyle(QgsComposition.Print)
c.renderPage( imagePainter, 0 )
imagePainter.end()

imageFilename =  mainPath + filename + '.' + imageType
image.save(imageFilename, imageType)
print 'image saved'
print 'done'