Enregistrer la vue Print / Map QGIS composer en PNG / PDF en utilisant Python (sans rien changer dans la disposition visible)?

11

J'ai un compositeur / vue d'impression QGIS ouvert avec tous les éléments ajustés comme je le souhaite. Maintenant, je dois imprimer / enregistrer / exporter cela en tant que fichier PNG ou PDF à partir de la console Python / du script Python.

Je ne veux rien changer dans la disposition actuelle. La plupart des exemples que j'ai trouvés (par exemple: ceci ), modifient une position ou une taille de carte dans le PDF de sortie par rapport à ce que je vois dans ma vue de compositeur actuelle. Je souhaite recevoir exactement le même résultat que j'obtiendrais en cliquant sur Imprimer -> Exporter en tant qu'image .

Comment puis je faire ça? Atlas n'est pas une solution pour moi.

matandked
la source

Réponses:

7

Je recommande d'utiliser le script suivant de Tim Sutton comme base, cela a bien fonctionné pour moi: - http://kartoza.com/how-to-create-a-qgis-pdf-report-with-a-few-lines- de-python /

Passez simplement un dict vide à la fonction composition.loadFromTemplate () au lieu de la «carte de substitution» qu'il utilise, car vous n'êtes pas intéressé par cette fonctionnalité.

De plus, puisque vous avez demandé «exporter en tant qu'image» plutôt que «exporter en PDF», vous devez faire un peu plus de travail que d'utiliser la fonction membre exportAsPDF ().

Vous trouverez ci-dessous une version modifiée du code de Tim qui devrait faire l'affaire, à partir d'un script Python externe. Définissez les variables de fichier DPI et project + composer comme requis.

(Si vous utilisez la console Python dans QGIS plutôt que de le faire en tant que script externe, vous pouvez obtenir le canevas de carte actuel en utilisant qgis.iface et vous n'avez pas besoin d'utiliser tout le code de chargement du projet, etc.).

import sys
from qgis.core import (
    QgsProject, QgsComposition, QgsApplication, QgsProviderRegistry)
from qgis.gui import QgsMapCanvas, QgsLayerTreeMapCanvasBridge
from PyQt4.QtCore import QFileInfo
from PyQt4.QtXml import QDomDocument

gui_flag = True
app = QgsApplication(sys.argv, gui_flag)

# Make sure QGIS_PREFIX_PATH is set in your env if needed!
app.initQgis()

# Probably you want to tweak this
project_path = 'project.qgs'

# and this
template_path = 'template.qpt'

# Set output DPI
dpi = 300

canvas = QgsMapCanvas()
# Load our project
QgsProject.instance().read(QFileInfo(project_path))
bridge = QgsLayerTreeMapCanvasBridge(
    QgsProject.instance().layerTreeRoot(), canvas)
bridge.setCanvasLayers()

template_file = file(template_path)
template_content = template_file.read()
template_file.close()
document = QDomDocument()
document.setContent(template_content)
ms = canvas.mapSettings())
composition = QgsComposition(ms)
composition.loadFromTemplate(document, {})
# You must set the id in the template
map_item = composition.getComposerItemById('map')
map_item.setMapCanvas(canvas)
map_item.zoomToExtent(canvas.extent())
# You must set the id in the template
legend_item = composition.getComposerItemById('legend')
legend_item.updateLegend()
composition.refreshItems()

dpmm = dpi / 25.4
width = int(dpmm * composition.paperWidth())
height = int(dpmm * composition.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(0)

# render the composition
imagePainter = QPainter(image)
composition.renderPage(imagePainter, 0)
imagePainter.end()

image.save("out.png", "png")

QgsProject.instance().clear()
QgsApplication.exitQgis()
patdevelop
la source
Je suis intéressé à utiliser votre code. Cependant, je reçois une carte vierge ... j'ai posté une question à ce sujet ici si vous avez des idées: gis.stackexchange.com/questions/216376/…
user25976
Cela m'intéresse également. Je pense que je suis assez proche, mais je reçois 'AttributeError: l'objet' QgsComposerItem 'n'a pas d'attribut' setMapCanvas '' Je ne sais pas où aller à partir de là.
jamierob
Comment puis-je trouver le code dans le lien dans cette réponse? J'ai essayé sur deux navigateurs différents et je n'arrive pas à trouver "l'exemple" auquel le post se réfère au-delàpython generate_pdf.py
raphael
3

Vous pouvez le programmer vous-même ou réutiliser une méthode du Maps Printerplugin. Je décrirai la deuxième option car elle est plus simple.

Vous devez avoir le Maps Printerplugin installé, ouvrir le projet QGIS avec votre composeur configuré, ouvrir la console QGIS Python et exécuter cet extrait de code (vous devrez l'ajuster avec vos propres paramètres dans la Your settingssection).

# Your settings
composerTitle = 'my composer' # Name of the composer you want to export
folder = '/path/to/export_folder/'
extension = '.png' # Any extension supported by the plugin

mp = qgis.utils.plugins['MapsPrinter']

for composer in iface.activeComposers():
    title = composer.composerWindow().windowTitle()
    if title == composerTitle:
        mp.exportCompo( composer, folder, title, extension )
        break

Après l'avoir exécuté, vous vous retrouverez avec un nouveau fichier dans /path/to/export_folder/my composer.png. Vous pouvez également utiliser '.pdf'comme extension pour exporter le compositeur au format PDF.

Germán Carrillo
la source
3

Jetez peut-être aussi un œil à mon autre réponse, car de nouveaux outils plus récents sont apparus depuis celui-ci.


Je suis arrivé au code ci-dessous qui n'est malheureusement pas entièrement fonctionnel. Ceci est basé sur la solution ci-dessus et sur ces autres questions:

Comment exporter par programmation une composition sous forme d'image?
Comment puis-je lire les paramètres de QgsPaperItem à partir de XML?
Enregistrer le canevas de carte au format PNG avec un fond transparent par programmation avec QGIS?

Mon code est capable d'extraire le fichier .qpt d'un fichier .qgs et de charger avec succès un compositeur à partir d'un modèle. Il imprime également un compositeur dans un fichier .png et affiche correctement les étiquettes et les formes stockées dans le compositeur.

Cependant, il ne parvient pas à charger tous les éléments liés à la carte et aux couches réelles (les étiquettes contenant l'expression de la couche ne sont pas dessinées non plus). Je pense que j'ai manqué un peu de savoir comment le projet doit être chargé et lié au compositeur.

Certaines personnes dans le commentaire de l' article original de Tim Sutton ont mentionné qu'elles étaient coincées au même stade sous Windows (c'est mon cas). C'est vraiment frustrant car j'ai l'impression que la réponse est vraiment très proche. Cher Internet, aidez-moi!

C'est aussi mes premières tentatives de python donc j'espère que vous serez gentil;)

#This python code aim to programmatically export the first composer stored in a qgs file using PyQgis API v 2.10
#Version 0.4 (non functional) WTFPL MarHoff 2015 - This code is mostly a "frankenstein" stub made with a lot of other snippets. Feel welcome to improve!
#Credits to gis.stackexchange community : drnextgis,ndawson,patdevelop,dakcarto,ahoi, underdark & Tim Sutton from kartoza
#More informations and feedback can be found at /gis/144792/

#This script assume your environement is setup for PyGis as a stand-alone script. Some nice hints for windows users : /gis//a/130102/17548

import sys
from PyQt4.QtCore import *
from PyQt4.QtXml import *
from qgis.core import *
from qgis.gui import *

gui_flag = True
app = QgsApplication(sys.argv, gui_flag)

# Make sure QGIS_PREFIX_PATH is set in your env if needed!
app.initQgis()

# Name of the .qgs file without extension
project_name = 'myproject'

#Important : The code is assuming that the .py file is in the same folder as the project
folderPath = QString(sys.path[0])+'/'
projectPath = QString(folderPath+project_name+'.qgs')
templatePath = QString(folderPath+project_name+'_firstcomposer.qpt')
imagePath = QString(folderPath+project_name+'.png')

#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)
composerAsElement = projectAsDocument.elementsByTagName("Composer").at(0).toElement()

#This block store the composer into a template file
templateFile = QFile(templatePath)
templateFile.open(QIODevice.WriteOnly)
out = QTextStream(templateFile)
#I need next line cause UTF-8 is somewhat tricky in my setup, comment out if needed
out.setCodec("UTF-8")
param = QString
composerAsElement.save(out,2)
templateFile.close()

#And this block load back the composer into a QDomDocument
#Nb: This is ugly as hell, i guess there is a way to convert a QDomElement to a QDomDocument but every attemps failed on my side...
composerAsDocument = QDomDocument()
composerAsDocument.setContent(templateFile)

#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, {})


#And lets print in our .png
image = composition.printPageAsRaster(0)
image.save(imagePath,'png')

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



J'ai supprimé ces lignes du code précédent car elles semblaient ne rien faire du tout. Ils n'ont engendré aucune erreur mais n'ont pas fait mieux.

# You must set the id in the template
map_item = composition.getComposerItemById('map')
map_item.setMapCanvas(canvas)
map_item.zoomToExtent(canvas.extent())
# You must set the id in the template
legend_item = composition.getComposerItemById('legend')
legend_item.updateLegend()
composition.refreshItems()

et ceux-ci sont également supprimés car ils semblaient inutiles lors de l'utilisation de printPageAsRaster ()

dpmm = dpi / 25.4
width = int(dpmm * composition.paperWidth())
height = int(dpmm * composition.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(0)    

# render the composition
imagePainter = QPainter(image)
composition.renderPage(imagePainter, 0)
imagePainter.end()
MarHoff
la source
Je viens de configurer une machine virtuelle avec Unbuntu 14.04 et ce code fonctionne comme un jeu d'enfant. Donc, le problème est lié à la version Windows de PyQgis (testé sur Window10 avec l'installation d'Osgeo4w64), je vais regarder si un ticket est déjà ouvert dans le traqueur de bogues.
MarHoff
1

Une autre option plus à jour serait de regarder ces 2 outils développés par la communauté QGIS. Comme Map-sprinter est activement pris en charge, vous devez vous attendre à ce que les scripts soient mis à jour avec les prochaines versions de QGIS.

Les deux outils fournissent une interface graphique mais les scripts sous-jacents sont écrits en Python

Exporter des compositeurs vers des fichiers - https://github.com/DelazJ/MapsPrinter
Exporter des compositeurs de plusieurs projets - https://github.com/gacarrillor/QGIS-Resources

MarHoff
la source