3

I have written a function in Python / PyQGIS (no standalone application, so copy/paste it to the Python console editor to run it) that takes a VectorLayer as argument, creates a MapRenderer object for that layer, then creates a MapComposer object using the MapRenderer, adds the map, a title and a legend to the composition and saves it as a png file.

Everything works fine except for the fact that the legend always includes all layers that are currently visible in the QGIS layer widget. What I like to have is to show only the layer in the legend that I passed to the MapRenderer (and then to the composition). See the following example.

That's what the QGIS layer widget looks like: Two layers are loaded, one is selected.

enter image description here

That is what the output image looks like when running the script below: Both layers from the layer widget are included in the legend instead of only the first layer (which is the only one visible in the plot).

enter image description here

This is my script. When trying to run it yourself, set the plotdir parameter at the top of it to match your desired output directory. The script does not load any layers, so this has to be done in advance.

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

plotdir = r'C:\pyqgis_plots\\'

def saveImagesWithMapComposer(layer):

    print 'saving images with map composer'

    #######
    # set up layer set, extent and create a map renderer
    #######
    mapRenderer = QgsMapSettings() #new in QGIS 2.4 - replaces QgsMapRenderer()
    #mapRenderer = QgsMapRenderer()
    layerset = [layer.id()]
    mapRenderer.setLayers(layerset) #when using QgsMapRenderer, replace this with setLayerSet(layerset)
    mapRectangle = mapRenderer.fullExtent()
    mapRenderer.setExtent(mapRectangle)
    mapRenderer.setOutputSize(QSize(1600,1200)) #when using QgsMapRenderer(), setOutputSize needs a second argument 'int dpi'

    #######
    # create a composition and pass the renderer
    #######
    comp = QgsComposition(mapRenderer)
    comp.setPlotStyle(QgsComposition.Print)
    dpi = comp.printResolution()
    dpmm = dpi / 25.4
    width = int(dpmm * comp.paperWidth())
    height = int(dpmm * comp.paperHeight())

    #######
    # add map to the composition
    #######
    x, y = 0, 0
    w, h = comp.paperWidth(), comp.paperHeight()
    composerMap = QgsComposerMap(comp, x, y, w, h)
    comp.addItem(composerMap)

    #######
    # create label
    #######
    composerLabel = QgsComposerLabel(comp)
    composerLabel.setText(layer.name())
    composerLabel.adjustSizeToText()
    comp.addItem(composerLabel)
    composerLabel.setItemPosition(20,10)

    #######
    # create legend
    #######
    composerLegend = QgsComposerLegend(comp)
    composerLegend.model().setLayerSet(mapRenderer.layers()) #when using QgsMapRenderer, use layerSet() instead of layers()
    composerLegend.setTitle('')
    comp.addItem(composerLegend)     
    composerLegend.setItemPosition(20,20)

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

    #######
    # Render composition
    #######
    imagePainter = QPainter(image)
    sourceArea = QRectF(0, 0, comp.paperWidth(), comp.paperHeight())
    targetArea = QRectF(0, 0, width, height)
    comp.render(imagePainter, targetArea, sourceArea)
    imagePainter.end()

    # Save image to disk (other extensions possible)
    image.save(plotdir + layer.name() + '.png', 'png')

    return

###################################
# call function
###################################

# get first layer from the canvas and pass it to the plot function
layers = iface.legendInterface().layers()
layer = layers[0]

saveImagesWithMapComposer(layer)

print 'finished'

I already tried using the deprecated QgsMapRenderer class instead of QgsMapSettings (see inline comments what has to be changed to try this) but the result is the same.

Any suggestions what I am missing here?

  • Uhm, I wrote the example above with QGIS dev 2.5 dbed468. I just tried to run it with QGIS 2.4.0 Chugiak and it works like it is supposed to :-O Still, I appreciate any suggestions what has to be changed to make this run on QGIS 2.5 so I can still use it with QGIS 2.6 when it is released. Meanwhile, I found out that with QGIS 2.6, QgsComposerLegend.model() is deprecated in favor of QgsComposerLegend.modelV2(), see the API docs. Maybe this is the new way to go? However, I could not manage yet to make it work this way... – Dirk Oct 29 '14 at 14:37
  • mapRenderer is deprecated in 2.4 and above see: gis.stackexchange.com/a/223127/36886 – raphael Jan 9 '17 at 20:46
  • Not an answer (mods please allow) but just happened on this thread whilst looking to export just a legend. Made light of what I though was going to be lots of hours/lines so thanks primarily to the OP and also those that answered. – Paul Jan 26 '17 at 11:28
2

Looking at your comment above, for QGIS 2.6 you can do something like this to insert a layer into the legend model:

   #######
   # create legend
   #######
   composerLegend = QgsComposerLegend(comp)
   layerGroup = QgsLayerTreeGroup()
   layerGroup.insertLayer(0, the_layer)
   composerLegend.modelV2().setRootGroup(layerGroup)
  • Hooray! This makes it work under QGIS 2.6, showing only the desired layers in the legend instead of all, thank you. – Dirk Nov 12 '14 at 11:04
  • composerLegend.modelV2().setRootGroup(layerGroup) Using Essen 2.14 on a Mac, this approach barely works. I get layers showing up but not the labels for groups/subgroups they're contained by. – Nona Yerbiz Apr 6 '16 at 21:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.