3

I have a QGIS project that has an Atlas print composer that I can use to generate multiple images (JPG) of each of my departments.

Now I would want to generate all those images but directly from a Linux server which does not have any graphical server (it is a headless server, like many Linux servers).

Is this possible?

Anthony O.
  • 239
  • 2
  • 8

1 Answers1

4

Here is what I finally did (also answered an other question with the script, but not the headless part).

Inspired by:

Save Print/Map QGIS composer view as PNG/PDF using Python (without changing anything in visible layout)?

How to export a configurated Atlas with a python script / command line?

QGIS: Automatisation de la génération d'un Atlas avec script python

Search for "[qgis] standalone script" on gis stackexchange

How to run a simple python script for QGIS from outside (e.g. Sublime Text)?

Using PyQGIS in custom applications

Generate a QGIS map PDF using python

How to create a QGIS PDF report with a few lines of python

QGIS Server Plugin Filters: Add a new request to print a specific atlas feature

QGIS export “save as image” automate with python?

I wrote the following exportAtlas.py python script:

#!/usr/bin/python
# -*- coding: utf-8 -*-
from qgis.core import QgsApplication, QgsProject, QgsComposition
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge
from PyQt4.QtCore import QFile, QFileInfo, QByteArray, QTextStream
from PyQt4.QtXml import QDomDocument
import os
import sys

projectPath = sys.argv[1] renderingPath = sys.argv[2]

def printAtlas(projectPath, renderingPath): # Inspired by Save Print/Map QGIS composer view as PNG/PDF using Python (without changing anything in visible layout)?, How to export a configurated Atlas with a python script / command line?, QGIS: Automatisation de la génération d'un Atlas avec script python, Search for "[qgis] standalone script" on gis stackexchange, How to run a simple python script for QGIS from outside (e.g. Sublime Text)?, Using PyQGIS in custom applications, Generate a QGIS map PDF using python, How to create a QGIS PDF report with a few lines of python, QGIS Server Plugin Filters: Add a new request to print a specific atlas feature, QGIS export “save as image” automate with python? #Getting project as Qfile and the first composer of the project as a QDomElement from the .qgs projectAsFile = QFile(projectPath) projectAsDocument = QDomDocument() projectAsDocument.setContent(projectAsFile) composerAsNode = projectAsDocument.elementsByTagName("Composer").at(0) # Only way to convert a QDomNode to a QDomDocument root, inspired by https://gis.stackexchange.com/a/164196/73088 & Convert QDomElement to QDomDocument and vs & read the documentation http://doc.qt.io/archives/qt-4.8/qtextstream.html & http://doc.qt.io/archives/qt-4.8/qdomnode.html . # Using a QByteArray because QString pointer can't be passed in python and QString is not available by default in QGIS python scripts QGIS PyQt4 missing QString class composerAsString = QByteArray() composerAsNode.save(QTextStream(composerAsString), 2) composerAsDocument = QDomDocument() composerAsDocument.setContent(composerAsString)

#Now that we got all we can open our project
canvas = QgsMapCanvas()
QgsProject.instance().read(QFileInfo(projectAsFile))
bridge = QgsLayerTreeMapCanvasBridge(
    QgsProject.instance().layerTreeRoot(), canvas)
bridge.setCanvasLayers()

#Lets try load that composer template we just extracted
composition = QgsComposition(canvas.mapSettings())
composition.loadFromTemplate(composerAsDocument, {})

atlas = composition.atlasComposition()
composition.setAtlasMode(QgsComposition.ExportAtlas)
print 'Found %d features to render.' % atlas.numFeatures()

atlas.beginRender()
for i in range(0, atlas.numFeatures()):
    print 'Rendering feature %d...' % i
    atlas.prepareForFeature(i)
    featureRenderingBasePath = os.path.join(renderingPath, str(format(i)))
    composition.exportAsPDF(featureRenderingBasePath + '.pdf')
    img = composition.printPageAsRaster(0)
    img.save(featureRenderingBasePath + '.jpg', 'jpg')
atlas.endRender()

#Some cleanup maybe?
QgsProject.instance().clear()


supply path to qgis install location

#QgsApplication.setPrefixPath("/usr", True) #already set in the right place "/usr" by default

create a reference to the QgsApplication

setting the second argument to True enables the GUI, which we need to do

since this is a custom application

qgs = QgsApplication([], True)

load providers

qgs.initQgis()

printAtlas(projectPath, renderingPath)

When your script is complete, call exitQgis() to remove the provider and

layer registries from memory

qgs.exitQgis()

On a headless server, one could use Xvfb to start a virtual X11 this way (before executing the previous python script): Xvfb :0 -ac -listen tcp -screen 0 2560x1440x24.

The python script can be called this way: DISPLAY=:0 python exportAtlas.py '/path/to/the/project.qgs' '/path/to/the/folder/where/to/generate/images/and/pdfs/'

In case the following error appears

>>> import qgis.core
ImportError: No module named qgis.core

like said in the documentation, you should define the PYTHONPATH environment variable to point to the python folder inside QGIS home. In my case it was:

export PYTHONPATH=/usr/share/qgis/python

I was able to script all of this using a Docker container with the following Dockerfile:

#FROM kartoza/qgis-desktop:2.18.20
FROM kartoza/qgis-desktop:2.18

RUN apt-get update &&
apt-get upgrade -y &&
apt-get install -y xvfb

ENV DISPLAY=:0

COPY exportAtlas.py /usr/local/bin/

then built it (docker build -t exportAtlas .) and ran it:

docker run -it --rm -v '/path/to/the/project/folder:/project' -v '/path/to/the/folder/where/to/generate/images/and/pdfs/:/exports' exportAtlas bash -c "PYTHONPATH=/usr/share/qgis/python python /usr/local/bin/exportAtlas.py '/project/qgis_project_filename.qgs' /exports/"

The problem with this script is that it doesn't take into consideration a layer which uses the Mask plugin, even if I unzip & install the zip to /usr/share/qgis/python/plugins.

I've fixed this in another answer.

Tom Chadwin
  • 5,842
  • 4
  • 25
  • 48
Anthony O.
  • 239
  • 2
  • 8
  • do you have a solution for qgis3? – AndreasK Dec 06 '18 at 22:14
  • @AndreasK I only tested this with Docker image kartoza/qgis-desktop:2.18... what error do you have with qgis3? – Anthony O. Dec 07 '18 at 10:08
  • Why you didn't used Qgis server as it's able to create atlas from a project. – SIGIS Jan 20 '21 at 09:36
  • @SIGIS I tested first to use Qgis server at the time I was searching for a solution to this problem, but at that time at least, I remember that some features (canvas? composer?) were not available in the server implementation in order to create such atlas... perhaps this is not the case anymore? In this reply I found that the Mask plugin (as well as others) was only compatible with Qgis (and not server, as far as I remember). – Anthony O. Jan 21 '21 at 14:10