Mit PyQGIS eine große Anzahl von Karten erstellen?

10

Ich muss eine große Anzahl (Hunderte) von Verbreitungskarten von Arten erstellen. Ich habe ein Shapefile, das die Verteilungen für jede Art enthält, und für jede möchte ich eine Karte als Bild (jpg, png oder auf andere Weise) erhalten, die den Namen der betreffenden Art, die Legende (zur Unterscheidung von Gebieten von) enthält jährliche Verteilung, Zucht, Nichtzucht usw. ...).

Ich würde gerne QGIS verwenden, um dies zu tun.

Onesime
quelle
1
Könnten Sie etwas mehr über die Besonderheiten der Karten sagen? Möchten Sie beispielsweise, dass alle diese Karten dasselbe Gebiet wie einen bestimmten Kontinent oder ein bestimmtes Land anzeigen, oder möchten Sie, dass sich die Kartenausdehnung dynamisch ändert? Möchten Sie auch alle Bereichsuntertypen auf einer Karte oder auf mehreren Karten? Abhängig von diesen Antworten kann Ihr Problem recht einfach sein oder einen etwas ausgefeilteren Ansatz erfordern. Ein guter Ausgangspunkt für die Suche ist das Atlas-Plugin für GIS. Wenn Sie ArcGIS 10 oder höher zur Verfügung haben, helfen auch die ESRI-Mapbooks.
Jay Guarneri
1
Entschuldigung für den Mangel an Details. Ja, für alle Karten ist es das gleiche Gebiet (Europa). Ich habe ein Shapefile mit allen Arten und in den Attributen die entsprechende Verteilung. Dieses Shapefile kann ich leicht in verschiedene Shapefiles aufteilen (eines für jede Art). Am Ende möchte ich für jede Art ein Bild mit jeweils genau demselben Gebiet (Europa) und denselben Farben haben (zum Beispiel jährliche Verteilungen in Dunkelgrün, Brut in Hellgrün, Nichtbrut in blau usw. ...), dieselbe Legende und als Titel den Namen der Art.
Onesime
Ich denke, Sie müssen jeden Schritt planen, den Sie zum Erstellen jeder Karte ausführen müssen, und dann die Auswahl und Kartenexporte in Python codieren. Ich weiß, dass dies in ArcGIS Python problemlos möglich ist, aber ich weiß nicht genug über die QGIS Python-Oberfläche, um viele Anleitungen zu geben. Ich bin jedoch zuversichtlich, dass Sie diese Funktion mit einem Shapefile ausführen können.
Jay Guarneri
Ich habe mit QGIS etwas Ähnliches mit einem Python-Plugin gemacht. In meinem Fall wurden meine Ebenen in PostGIS gespeichert, aber ich denke, Sie könnten mit einem Shapefile etwas Ähnliches tun. Ich teile gerne meinen Code. Pm Ich.
Brian Edmond
1
Können Sie ein Beispiel Ihrer Daten hochladen, damit wir damit spielen können?
Nathan W

Antworten:

4

Ich hatte eine ähnliche Anforderung und stellte ein QGIS-Plugin zusammen, um die Karten zu generieren, basierend auf einem Shapefile mit Punktlokalitäten für alle Arten (es wird ein eindeutiger Taxonname in der Attributtabelle als gemeinsamer Bezeichner angenommen). Meine Anforderungen waren nicht so komplex - ich brauchte keine saisonalen Informationen, Titel oder Legenden, aber es könnte ein nützlicher Ausgangspunkt für Sie sein. Für die komplexeren Aspekte müssen Sie den Map Composer verwenden. Weitere Informationen hierzu finden Sie im PyQGIS-Kochbuch .

Plugin

Das Plugin automatisiert die Erstellung der Karten und ermöglicht Ihnen die Konfiguration von Ausmaßen, Auflösung und anderen Aspekten. Es wendet denselben Stil auf die Ausgabe an wie Ihre Rasterüberlagerung. Derzeit läuft es nur auf der Entwicklungsversion von QGIS (1.9 oder höher).

Sextante-Skript

Bevor ich das Plugin gemacht habe, habe ich die Logik mit SEXTANTE ausgearbeitet. Dieses Benutzerskript sollte auch in Version 1.8 funktionieren (habe es nicht getestet). Die Verteilungsstildatei (.qml) ist der Stil der Ausgabeverteilungen (sie ignoriert den Stil der Verteilungsüberlagerung). Derzeit werden Ausgabekarten basierend auf den Standardeinstellungen Ihres Betriebssystems im temporären Verzeichnis abgelegt (/ tmp unter Linux und an verschiedenen Stellen in Windows - definiert durch die Umgebungsvariable TEMP). Sie können dies jedoch ziemlich einfach selbst im Code definieren. Sie müssen auch den Umfang und die Ausgabeauflösung im Code bearbeiten (und die Hintergrundfarbe, wenn Sie eine andere Farbe für das Meer wünschen).

#Definition of inputs and outputs
#==================================
##[Scratch]=group
##all_localities=vector
##taxon_field=field all_localities
##africa_map=vector
##sa_map=vector
##grid_layer=vector
##distribution_style_file=file

#Algorithm body
#==================================
from qgis.core import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sextante.core.QGisLayers import QGisLayers
from sextante.core.SextanteVectorWriter import SextanteVectorWriter
import tempfile
import os

def print_map(taxon,taxon_shp):
    #load taxon layer (necessary?)
    #QGisLayers.load(taxon_shp,name = "taxon",style = distribution_style_file)
    taxon_layer = QgsVectorLayer(taxon_shp,"taxon","ogr")
    QgsMapLayerRegistry.instance().addMapLayer(taxon_layer)
    taxon_layer.loadNamedStyle(distribution_style_file)

    # create image (dimensions 325x299)
    img = QImage(QSize(325,299), QImage.Format_ARGB32_Premultiplied)

    # set image's background color
    color = QColor(192,192,255)   # blue sea
    img.fill(color.rgb())

    # create painter
    p = QPainter()
    p.begin(img)
    p.setRenderHint(QPainter.Antialiasing)

    render = QgsMapRenderer()

    # create layer set
    africa_layer = QGisLayers.getObjectFromUri(africa_map)
    sa_layer = QGisLayers.getObjectFromUri(sa_map)
    #taxon_layer = QGisLayers.getObjectFromUri(taxon_shp)

    lst = []
    lst.append(taxon_layer.id())    
    lst.append(sa_layer.id())
    lst.append(africa_layer.id())

    render.setLayerSet(lst)

    # set extent (xmin,ymin,xmax,ymax)
    rect = QgsRectangle(14.75,-36.00,34.00,-21.00)
    render.setExtent(rect)

    # set output size
    render.setOutputSize(img.size(), img.logicalDpiX())

    # do the rendering
    render.render(p)
    p.end()

    # save image
    #outdir = os.path.dirname(os.path.abspath(output))
    tempdir = tempfile.gettempdir()
    img.save(os.path.join(tempdir,taxon+".png"),"png")

    # remove taxon layer from project
    QgsMapLayerRegistry.instance().removeMapLayers([taxon_layer.id()])

tempdir = tempfile.gettempdir()   
taxa = sextante.runalg('qgis:listuniquevalues', all_localities, taxon_field, None)['UNIQUE_VALUES'].split(";")
for taxon in taxa:
    sextante.runalg('qgis:selectbyattribute', all_localities, taxon_field, 0, taxon)
    sextante.runalg('qgis:selectbylocation', grid_layer, all_localities, 0)
    filename = os.path.join(tempdir,"taxon.shp")    #memory file better?
    sextante.runalg('qgis:saveselectedfeatures', grid_layer, filename)
    print_map(taxon,filename)
rudivonstaden
quelle
Hallo zusammen, vielen Dank für all Ihre Antworten. Um Ihnen einige weitere Elemente zu geben, handelt es sich um Daten, die von BirdLife stammen (ein Beispiel für eine Art: birdlife.org/datazone/speciesfactsheet.php?id=2794 ). Kurz gesagt, es gibt ein Shapefile mit allen Polygonen für alle Arten (für einige von ihnen also viele Linien für eine einzelne Art), und es gibt ein Attribut, das der saisonalen Verteilung entspricht (wobei Werte von 1 bis 5 unterschiedlichen Verwendungen entsprechen ), eine andere für den Ursprung etc .. Die Legende und der Titel sind nicht unverzichtbar.
Onesime
- Ich verwende eine Länderebene im Hintergrund, nur für einen einfachen Standort. - Für die unterschiedliche Farbe für den unterschiedlichen Wert aus dem Attribut "saisonal" halte ich dafür eine .qml-Datei für geeignet. - Optional muss ich für den Titel und die Legende eine Datei von Composer verwenden. Wenn es zu schwierig ist, kann ich sie mit einer anderen Software hinzufügen. - Der Vorgang muss für alle Arten wiederholt werden, dies entspricht also einer Auswahl nach Attributen, anhand derer der Name des endgültigen Bildes angegeben wird.
Onesime
Ich habe das Plugin "Atlas" ausprobiert, aber es scheint für verschiedene Standorte besser geeignet zu sein. In meinem Fall ist es immer für denselben Bereich: Europa. Ich habe das Plugin "Distribution Map Manager" ausprobiert, das an dieser Stelle zu entsprechen scheint, da es möglich ist, den Abdeckungsbereich zu korrigieren, aber ich brauche nicht den Prozess, der Punkte mit einer Gitterebene schneidet, da ich bereits eine Polygonebene habe. Ich habe es in ArcGis versucht, aber es ist ziemlich dasselbe für das QGis Atlas-Plugin. Die Lösung scheint darin zu bestehen, ein Python-Skript zu schreiben ...
Onesime
Ich denke also, ich werde Sextante verwenden, basierend auf dem "rudivonstaden" -Skript (danke dafür!) Und es an meinen Fall anpassen. Schließlich entschuldigen Sie diese unterschiedlichen Kommentare, aber es gibt eine Beschränkung der
Zeichennummer
@Onesime, mit Ausnahme des Titels und der Legende, denke ich, dass Sie das obige Sextante-Skript anpassen können, um das zu tun, was Sie brauchen. Sie müssen wahrscheinlich den selectbylocationSchritt entfernen selectbyattributeund saveselectedfeaturesfür jede Jahreszeit einen zusätzlichen und einen Schritt hinzufügen (wechseln grid_layerzu all_localities). Laden Sie dann weitere XML-Dateien und fügen Sie Ihre saisonalen Shapefiles hinzu (oberste Ebene zuerst angehängt). Wenn Sie sich nicht sicher sind, wie, könnte ich wahrscheinlich versuchen, das obige Skript so zu bearbeiten, dass es mehr oder weniger funktioniert.
Rudivonstaden
2

Ich habe heute wenig Zeit gebraucht, um daran zu arbeiten. Also habe ich einige Änderungen an Ihrem Skript vorgenommen. Ich muss keinen zusätzlichen Schritt selectbyattribute hinzufügen und ausgewählte Funktionen speichern, da ich XML-Dateien verwende und das Feld Seasonal im selben Shapefile ist. Unten können Sie sehen, was ich getan habe:

#Definition of inputs and outputs
#==================================
##[Scratch]=group
##all_localities=vector
##taxon_field=field all_localities
##seasonal_field=field all_localities
##countries_map=vector
##distribution_style_file=file
##output_folder=folder

#Algorithm body
#==================================
from qgis.core import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from sextante.core.QGisLayers import QGisLayers
from sextante.core.SextanteVectorWriter import SextanteVectorWriter
import tempfile
import os

def print_map(taxon,taxon_shp):
#load taxon layer (necessary?)
#QGisLayers.load(taxon_shp,name = "taxon",style = distribution_style_file)
taxon_layer = QgsVectorLayer(taxon_shp,"taxon","ogr")
QgsMapLayerRegistry.instance().addMapLayer(taxon_layer)
taxon_layer.loadNamedStyle(distribution_style_file)

# create image (dimensions 325x299)
img = QImage(QSize(325,299), QImage.Format_ARGB32_Premultiplied)

# set image's background color
color = QColor(221,249,254)   # blue sea
img.fill(color.rgb())

# create painter
p = QPainter()
p.begin(img)
p.setRenderHint(QPainter.Antialiasing)

render = QgsMapRenderer()

# create layer set
countries_layer = QGisLayers.getObjectFromUri(countries_map)
taxon_layer = QGisLayers.getObjectFromUri(taxon_shp)

lst = []
lst.append(taxon_layer.id())    
lst.append(countries_layer.id())
render.setLayerSet(lst)

# set extent (xmin,ymin,xmax,ymax)
rect = QgsRectangle(-11,32,39,71)
render.setExtent(rect)
# set output size
render.setOutputSize(img.size(), img.logicalDpiX())

# do the rendering
render.render(p)
p.end()

#save image
#outdir = os.path.dirname(os.path.abspath(output))
tempdir = output_folder
img.save(os.path.join(tempdir,taxon+".png"),"png")

# remove taxon layer from project
QgsMapLayerRegistry.instance().removeMapLayers([taxon_layer.id()])

tempdir = tempfile.gettempdir()  

taxa = sextante.runalg('qgis:listuniquevalues', all_localities, taxon_field, None)        ['UNIQUE_VALUES'].split(";")

for taxon in taxa:
sextante.runalg('qgis:selectbyattribute', all_localities, taxon_field, 0, taxon)
filename = os.path.join(tempdir,"taxon.shp")    #memory file better?
sextante.runalg('qgis:saveselectedfeatures', all_localities, filename)
print_map(taxon,filename)

Wenn Sie eine Bemerkung oder einen Rat zur Verbesserung haben, zögern Sie nicht.

Um dies zu verbessern, ist es am besten, wenn wir das Ausmaß auswählen (zum Beispiel Europa), dieses Ausmaß zu verwenden, um nur Arten auszuwählen, die in diesem Ausmaß enthalten sind. Dies liegt daran, dass ich Karten für alle Arten erhalte, auch für solche außerhalb Europas (also habe ich viele leere Karten). Denkst du das ist möglich?

Prost,

Onesime

Onesime
quelle