Community • 19. August 2022

Netzwerkanfragen in PyQGIS richtig programmieren

Für unsere Kunden programmieren wir in Python oft QGIS-Plugins nach deren individuellen Bedürfnissen und Wünschen. Zu diesen gehört zum Beispiel das Geokodieren von Orten oder Adressen. Dafür kommt Nominatim zum Einsatz. Was auf der OpenStreetMap-Webseite möglich ist, lässt sich auch über eine Programmierschnittstelle aufrufen.

Zur Inbetriebnahme von Nominatim muss im Hintergrund der Entwicklungsumgebung die folgende URL aufgerufen werden, welche die Ergebnisse im JSON-Format zurückliefert: https://nominatim.openstreetmap.org/search?q=WhereGroup&format=json

Die möglichen Funktionen und Parameter können auf der Webseite von Nominatim nachgeschlagen werden. Was manuell im Browser möglich ist, kann darüber hinaus mit Python in QGIS automatisiert werden. Anstelle des Browsers wird dann eine Softwarebibliothek verwendet und das Ergebnis in der Konsole ausgegeben. Welches Python Modul am besten dafür verwendet wird und welches nicht, soll Thema dieses Beitrags sein.

Anfragen mit PyQGIS – der Holzweg

In Python gibt es die notwendigen Bibliotheken für solche Aufgaben:

import urllib.request
url = 'https://nominatim.openstreetmap.org/search?q=WhereGroup&format=json'
request = urllib.request.urlopen(url)
print(request.read())

Das war es schon? Theoretisch ja, aber leider nein, denn robust ist dieser Ansatz nicht. In den Hinweisen für die Plugin-Entwicklung für QGIS  wird ausdrücklich davon abgeraten die üblichen Python-Module wie urllib oder requests zu verwenden, weil es hier oft zu Verbindungsproblemen kommt, da so die ggf. in QGIS oder im System konfigurierten Proxyeinstellungen ignoriert werden. Was für die meisten Nutzer*innen funktioniert, kann bei Internetnutzer*innen, die auf einen Proxy angewiesen sind, zu folgendem Fehler führen:

407 Proxy Authentication Required

In diesem Fall sind die Proxyeinstellungen leider nicht korrekt übernommen worden, daher kommt es zum Problem beim Zugriff auf das Internet. Für den/die Nutzer*in ist das Problem schwer verständlich, weil für ihn/sie alles funktioniert, aber nicht in der Software. Ähnliche Probleme können durch Weiterleitungen verursacht werden. Was bis QGIS-Version 3.10 die Nutzung des „Network Access Manager“ notwendig machte, ist nun direkt mit QGIS-eigenen Klassen möglich.

Anfragen mit PyQGIS – der korrekte Weg

In den QGIS-Versionen 3.4 bis 3.10 kamen die folgenden Klassen zum Funktionsumfang von PyQGIS hinzu: QgsNetworkAccessManager, QgsBlockingNetworkRequest und QgsNetworkContentFetcher. Sie bieten umfangreiche Möglichkeiten zum Zugriff auf Netzwerkressourcen mit Optionen für Caching und Authentifizierung. Der große Vorteil dieser Klassen ist, dass sie Einstellungen aus dem Betriebssystem und von QGIS übernehmen können und damit etwaige Proxyeinstellungen keine Hürde mehr darstellen.

QgsNetworkAccessManager

Wenn die volle Kontrolle über weitere Proxyeinstellungen, Cache und bedingte Weiterleitungen benötigt wird, dann ist der QgsNetworkAccessManager das Tool der Wahl. Es lassen sich damit auch einfache Anfragen wie unser Nominatim-Beispiel umsetzen:

url = 'https://nominatim.openstreetmap.org/search?q=WhereGroup&format=json'
request = QNetworkRequest(QUrl(url))
reply = QgsNetworkAccessManager.instance().blockingGet(request)

if reply.error():
    if reply.errorString():
       print(reply.errorString())
if reply.content():
    print(reply.content())

Der tatsächliche Funktionsumfang der Klasse ist dabei in diesem einfachen Beispiel noch lange nicht ausgeschöpft. Es wird eine GET-Anfrage gestellt, was heißt, dass die Parameter in die URL kodiert sind. Das „blocking“ bedeutet, dass die weitere Code-Ausführung angehalten wird, bis die Anfrage vom Server beantwortet wurde oder es zu einer Zeitüberschreitung kommt.

QgsBlockingNetworkRequest

Für den hier beschriebenen Anwendungsfall ist der QgsBlockingNetworkRequest das beste Werkzeug, weil keine weiteren Einstellungen notwendig sind und auf das Ergebnis der Anfrage (“blocking“) gewartet werden soll.

url = 'https://nominatim.openstreetmap.org/search?q=WhereGroup&format=json'
request = QgsBlockingNetworkRequest()
error_code = request.get(QNetworkRequest(QUrl(url)))

if error_code == QgsBlockingNetworkRequest.ErrorCode.NoError:
    repley = request.reply()
    print(reply.content())
else:
    print(error_code)

QgsNetworkContentFetcher

Der QgsNetworkContentFetcher soll hier mehr der Vollständigkeit halber mit erwähnt werden. Wie der Name schon vermuten lässt, geht es bei dieser Klasse nicht um „rohe“ Anfragen, deren Antworten anschließend durch weiteren Programmcode im Detail verarbeitet werden, sondern um das Herunterladen von Dateien. Falls größere Dateien im Hintergrund, also nicht blockierend, heruntergeladen werden sollen, dann ist sie die beste Wahl. In der graphischen Oberfläche von QGIS gibt es auch direkt das passende Verarbeitungswerkzeug mit Oberfläche dazu: „Datei herunterladen“.

Fazit

Für den Zugriff auf Netzwerkressourcen mit Python in QGIS sollten immer QgsNetworkAccessManager, QgsBlockingNetworkRequest und QgsNetworkContentFetcher je nach den genauen Anforderungen genutzt werden. Dabei sollte, wenn möglich auf HTTPS statt auf HTTP gesetzt werden. Die hier gezeigten Codebeispiele lassen sich ohne das Laden weiterer Module in der Python-Konsole von QGIS ausführen. In Skripten müssen diese aber separat geladen werden. Übrigens kann bei der Verwendung der QGIS-eigenen Funktionen der resultierende Netzwerkverkehr im Diagnose-/Entwicklungswerkzeugepanel (F12-Taste) eingesehen werden, ein weiterer guter Grund sie einzusetzen.

Weitere Beiträge, die Dich interessieren könnten:

Dr. -Ing. Mathias Gröbe

Mathias Gröbe hat Kartographie an der TU Dresden studiert und arbeitet als GIS-Experte bei der WhereGroup. Er ist in Dresden zu hause und verbindet seine Wanderungen gerne mit dem Ergänzen von fehlenden Informationen in OpenStreetMap.

Artikel teilen:

Tags