Speichern / Drucken der QGIS-Composer-Ansicht als PNG / PDF mit Python (ohne Änderungen am sichtbaren Layout vorzunehmen)?


11

Ich habe eine QGIS-Composer- / Druckansicht geöffnet, in der alle Elemente nach meinen Wünschen angepasst sind. Jetzt muss ich dies als PNG- oder PDF-Datei aus der Python-Konsole / dem Python-Skript drucken / speichern / exportieren.

Ich möchte nichts am aktuellen Layout ändern. Die meisten Beispiele, die ich gefunden habe (zum Beispiel: dies ), ändern eine Kartenposition oder -größe in der Ausgabe-PDF im Vergleich zu dem, was ich in meiner aktuellen Komponistenansicht sehe. Ich möchte genau das gleiche Ergebnis erhalten, das ich erhalten würde, wenn ich auf Drucken -> Als Bild exportieren klicke .

Wie kann ich das machen? Atlas ist für mich keine Lösung.

Antworten:


7

Ich empfehle das folgende Skript von Tim Sutton als Basis zu verwenden, es hat für mich gut funktioniert: - http://kartoza.com/how-to-create-a-qgis-pdf-report-with-a-few-lines- of-python /

Übergeben Sie einfach ein leeres Diktat an die Funktion compos.loadFromTemplate () anstelle der von ihm verwendeten 'Substitutionszuordnung', da Sie an dieser Funktionalität nicht interessiert sind.

Da Sie eher nach "Als Bild exportieren" als nach "Als PDF exportieren" gefragt haben, müssen Sie etwas mehr Arbeit leisten als die Mitgliedsfunktion exportAsPDF ().

Unten finden Sie eine modifizierte Version von Tims Code, die den Trick ausführen sollte und mit einem externen Python-Skript funktioniert. Stellen Sie die Variablen DPI und Projekt + Composer-Datei nach Bedarf ein.

(Wenn Sie die Python-Konsole in QGIS verwenden, anstatt dies als externes Skript auszuführen, können Sie den aktuellen Kartenbereich mithilfe der Datei qgis.iface abrufen und müssen nicht den gesamten Code zum Laden usw. des Projekts verwenden.)

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()

Ich bin daran interessiert, Ihren Code zu verwenden. Ich bekomme jedoch eine leere Karte ... Ich habe hier eine Frage dazu gestellt, wenn Sie irgendwelche Ideen haben: gis.stackexchange.com/questions/216376/…
user25976

Ich bin auch daran interessiert, dies zu nutzen. Ich denke, ich bin ziemlich nah dran, aber ich bekomme 'AttributeError:' QgsComposerItem 'Objekt hat kein Attribut' setMapCanvas '' Ich weiß nicht, wohin ich von dort aus gehen soll.
Jamierob

Wie finde ich den Code im Link in dieser Antwort? Ich habe zwei verschiedene Browser ausprobiert und kann das "Beispiel", auf das sich der Beitrag bezieht, nicht findenpython generate_pdf.py
raphael

3

Sie können es selbst programmieren oder eine Methode aus dem Maps PrinterPlugin wiederverwenden . Ich werde die zweite Option beschreiben, weil es einfacher ist.

Sie müssen das Maps PrinterPlugin installiert haben, das QGIS-Projekt mit Ihrem konfigurierten Composer öffnen, die QGIS Python-Konsole öffnen und dieses Code-Snippet ausführen (Sie müssten es mit Ihren eigenen Einstellungen im Your settingsAbschnitt anpassen ).

# 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

Nach dem Ausführen erhalten Sie eine neue Datei in /path/to/export_folder/my composer.png. Sie können '.pdf'den Composer auch als Erweiterung verwenden, um ihn als PDF zu exportieren.


3

Vielleicht werfen Sie auch einen Blick auf meine andere Antwort, da seitdem neue, aktuellere Tools entstanden sind.


Ich bin zu dem Code gekommen, der leider nicht voll funktionsfähig ist. Dies basiert auf der obigen Lösung und auf diesen anderen Fragen:

Wie exportiere ich eine Komposition programmgesteuert als Bild?
Wie kann ich die Einstellungen für das QgsPaperItem aus XML lesen?
Speichern von Map Canvas als PNG mit transparentem Hintergrund programmgesteuert mit QGIS?

Mein Code kann die .qpt-Datei aus einer .qgs-Datei extrahieren und einen Composer erfolgreich aus der Vorlage laden. Außerdem wird ein Komponist in eine PNG-Datei gedruckt und die im Komponisten gespeicherten Beschriftungen und Formen werden korrekt angezeigt.

Es können jedoch nicht alle Elemente geladen werden, die sich auf die tatsächliche Karte und Ebenen beziehen (Beschriftungen, die Ausdrücke aus Ebenen enthalten, werden ebenfalls nicht gezeichnet). Ich glaube, ich habe ein bisschen übersehen, wie das Projekt geladen und mit dem Komponisten verknüpft werden muss.

Einige Leute im Kommentar des Originalartikels von Tim Sutton erwähnten, dass sie unter Windows im selben Stadium feststeckten (es ist mein Fall). Das ist wirklich frustrierend, weil ich das Gefühl habe, dass die Antwort wirklich sehr nahe ist. Liebes Internet bitte helfen!

Auch dies sind meine ersten Versuche mit Python, also hoffe ich, dass du nett bist;)

#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()



Ich habe diese Zeilen aus dem vorherigen Code entfernt, da sie anscheinend überhaupt nichts zu tun hatten. Sie haben keinen Fehler hervorgebracht, aber es nicht besser gemacht.

# 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()

und diese werden ebenfalls entfernt, da sie bei Verwendung von printPageAsRaster () unnötig erschienen.

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()

Ich habe gerade eine VM mit Unbuntu 14.04 eingerichtet und dieser Code funktioniert wie ein Kinderspiel. Das Problem hängt also mit der Windows-Version von PyQgis zusammen (getestet unter Windows 10 mit Osgeo4w64-Installation). Ich werde prüfen, ob im Bug-Tracker bereits ein Ticket geöffnet ist.
MarHoff

1

Eine weitere aktuellere Option wäre die Betrachtung dieser beiden von der QGIS-Community entwickelten Tools. Da Map-Sprinter aktiv unterstützt wird, sollten Sie damit rechnen, dass die Skripte mit den kommenden Versionen von QGIS aktualisiert werden.

Beide Tools bieten eine GUI, aber die zugrunde liegenden Skripte sind in Python geschrieben

Komponisten in Dateien exportieren - https://github.com/DelazJ/MapsPrinter
Exportieren Sie Komponisten aus mehreren Projekten - https://github.com/gacarrillor/QGIS-Resources

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.