I would like to 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
In order to solve this question, we need to use timers or something that delays the execution of the script, so the canvas can reflect the layer arrangement at the time the map is exported. In other words, if you don't use timers you'll end up with 3 PNG images with the same content because everything will happen too fast.
In the QGIS map, set the map extent you want to export before running the following script in the QGIS Python Console (adjust the path ):
from PyQt4.QtCore import QTimer
fileName = '/tmp/exported' # exported is a prefix for the file names
boundaryLayer = QgsMapLayerRegistry.instance().mapLayersByName('boundary')[0]
climitsLayer = QgsMapLayerRegistry.instance().mapLayersByName('climits')[0]
otherLayers = ['Div1_Irrig_1956_0', 'Div1_Irrig_1956_1', 'Div1_Irrig_1956_2']
count = 0
iface.legendInterface().setLayerVisible(boundaryLayer, True)
iface.legendInterface().setLayerVisible(climitsLayer, True)
def prepareMap(): # Arrange layers
iface.actionHideAllLayers().trigger() # make all layers invisible
iface.legendInterface().setLayerVisible(QgsMapLayerRegistry.instance().mapLayersByName( otherLayers[count] )[0], True)
QTimer.singleShot(1000, exportMap) # Wait a second and export the map
def exportMap(): # Save the map as a PNG
global count # We need this because we'll modify its value
iface.mapCanvas().saveAsImage( fileName + "_" + str(count) + ".png" )
print "Map with layer",count,"exported!"
if count < len(otherLayers)-1:
QTimer.singleShot(1000, prepareMap) # Wait a second and prepare next map
count += 1
prepareMap() # Let's start the fun
After the execution of the script, you'll end up with 3 (different) PNG images in /tmp/
.
If you need to iterate over more layers, you just need to add their names to the otherLayers
list, the script will do the rest for you.
----------------------------------------------------------------
EDIT: How to run this as a standalone script (outside of QGIS)?
The following script can be run outside of QGIS. Just make sure you adjust the file paths to your own directory structure and that you use a QGIS prefix that works for your own environment (see this answer for details):
from qgis.core import QgsApplication, QgsMapLayerRegistry, QgsVectorLayer, QgsProject
from qgis.gui import QgsMapCanvas, QgsMapCanvasLayer, QgsLayerTreeMapCanvasBridge
from PyQt4.QtCore import QTimer, QSize
qgisApp = QgsApplication([], True)
qgisApp.setPrefixPath("/usr", True)
qgisApp.initQgis()
# Required variables with your shapefile paths and names
pngsPath = '/tmp/'
boundaryLayer = QgsVectorLayer('/docs/geodata/colombia/colombia_wgs84.shp', 'boundary', 'ogr')
climitsLayer = QgsVectorLayer('/docs/geodata/colombia/colombia-geofabrik/railways.shp', 'climits', 'ogr')
otherLayers = {'Div1_Irrig_1956_0': QgsVectorLayer('/docs/geodata/colombia/colombia-geofabrik/points.shp', 'Div1_Irrig_1956_0', 'ogr'),
'Div1_Irrig_1956_1':QgsVectorLayer('/docs/geodata/colombia/colombia-geofabrik/places.shp', 'Div1_Irrig_1956_1', 'ogr'),
'Div1_Irrig_1956_2': QgsVectorLayer('/docs/geodata/colombia/colombia-geofabrik/natural.shp', 'Div1_Irrig_1956_2', 'ogr')}
count = 0
canvas = QgsMapCanvas()
canvas.resize(QSize(500, 500)) # You can adjust this values to alter image dimensions
canvas.show()
# Add layers to map canvas taking the order into account
QgsMapLayerRegistry.instance().addMapLayer( boundaryLayer)
QgsMapLayerRegistry.instance().addMapLayers( otherLayers.values() )
QgsMapLayerRegistry.instance().addMapLayer( climitsLayer )
layerSet = [QgsMapCanvasLayer(climitsLayer)]
layerSet.extend([QgsMapCanvasLayer(l) for l in otherLayers.values() ])
layerSet.append(QgsMapCanvasLayer(boundaryLayer))
canvas.setLayerSet( layerSet )
# Link Layer Tree Root and Canvas
root = QgsProject.instance().layerTreeRoot()
bridge = QgsLayerTreeMapCanvasBridge(root, canvas)
def prepareMap(): # Arrange layers
for lyr in otherLayers.values(): # make all layers invisible
root.findLayer( lyr.id() ).setVisible(0) # Unchecked
root.findLayer( otherLayers.values()[count].id() ).setVisible(2) # Checked
canvas.zoomToFullExtent()
QTimer.singleShot(1000, exportMap) # Wait a second and export the map
def exportMap(): # Save the map as a PNG
global count # We need this because we'll modify its value
canvas.saveAsImage( pngsPath + otherLayers.keys()[count] + ".png" )
print "Map with layer",otherLayers.keys()[count],"exported!"
if count < len(otherLayers)-1:
QTimer.singleShot(1000, prepareMap) # Wait a second and prepare next map
else: # Time to close everything
qgisApp.exitQgis()
qgisApp.exit()
count += 1
prepareMap() # Let's start the fun
qgisApp.exec_()
Again, if you need to iterate over more layers, just add them to the otherLayers
dictionary, the script will do the rest.
The resulting PNG image file names will correspond to your layers.
No comments:
Post a Comment