Wie bereits erwähnt wollte ich den WFS Altimeter mit Python ansprechen und nicht über die Javamaske des BKG bzw. im Browser. Da man den WFS nur mit POST-Requests füttern kann ist letzteres ein mühsames Unterfangen. Deshalb musste der Dienst mit Python angesprochen werden.

Nach einer kurzen Suche fand ich OWSLib für Python. Es gibt also bereits ein Pythonmodul, dass sich dem Thema OGC Web Services (OWS) angenommen hat. Damit können WMS und WFS in Python angesprochen werden, ohne dass man sich durch die XMLs der Dienste hindurch parsen müsste.

„Schön, wenn man das Rad nicht neu erfinden muss“, dachte ich mir und fing an zu testen. Leider musste ich dann bald feststellen, dass OWSLib nur mit WFS in der Version 1.0.0 klar kommt. Da war doch was… Genau! Der WFS Altimeter läuft nur (und das ziemlich stur) in der Version 1.1.0. Diese eine kleine Zahl kann doch nicht so tragisch ein, oder?

Doch… In den WFS-Spezifikationen, die ich nur grob verglichen hatte war es unschwer zu erkennen, dass sich eigentlich alle beschreibenden Elemente des WFS in den beiden Versionen unterscheiden. In 1.1.0 gibt es komplett andere Prefixe bei den XML-Tags, die natürlich das OWSLib-Modul schon beim Einlesen des XML-Capabilities-Dokuments durcheinander bringen.

Na gut, dann mach ichs halt selbst. Meine TODO-Liste bei dem Pythonscript sah folgendermaßen aus.

Klären, wie man in Python

  • einen HTTP-Request absetzt und dabei HTTP-POST verwendet und nicht HTTP-GET
  • die Antwort des Servers bekommt

Mehr brauchts ja erstmal nicht. Da ich noch keinerlei Erfahrung mit Python und Web Services gemacht hatte habe ich etwas im Internet recherchiert.

Das Prinzip ist eigentlich recht einfach – man muss eben nur wissen wie. Zunächst brauchen wir das Modul urllib2, das bei jeder Python-Installation dabei ist. Anschließend wird ein sogenannter Opener instanziiert und der eigentliche XML-POST-Request als Text der Variablen xmlrequest zugewiesen. Dabei sollte man auf die drei Anführungszeichen setzen, die es einem in Python ermöglichen, längere Texte über mehrere Zeilen zu schreiben.

import urllib2
opener = urllib2.build_opener()
xmlrequest ='''<wfs:GetFeature service="wfs" version="1.1.0" outputFormat="text/xml; subtype=gml/3.1.1" maxFeatures="10"
xmlns:gdz="http://www.geodatenzentrum.de/altimeter"
xmlns:wfs="http://www.opengis.net/wfs"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.opengis.net/wfs http://schemas.opengis.net/wfs/1.1.0/wfs.xsd">
<wfs:Query typeName="gdz:Altitude">
<ogc:Filter>
<ogc:Equals>
<ogc:PropertyName>gdz:location</ogc:PropertyName>
<gml:Point><gml:pos>3651650.00 5971150.00</gml:pos></gml:Point>
</ogc:Equals>
</ogc:Filter>
</wfs:Query>
</wfs:GetFeature>'''

Jetzt kann der Request mit der Basis-URL und dem XML-Teil des POST-Request aufgebaut werden. Danach sollte auf jeden Fall drauf geachtet werden, dass man den Header des erstellten Requests mit einem Content-Tag versieht, der den Server, über den Inhalt des Requests (XML) aufklärt.

req = urllib2.Request("http://gdz.bkg.bund.de/wfs_altimeter__GUID",xmlrequest)
req.add_header('Content-Type', 'text/xml')

Nun wird dem Opener der Request übergeben und die Antwort des Servers in die Variable wfsResponse eingelesen.

response = opener.open(req)
wfsResponse = response.read()
print wfsResponse

Mit diesen wenigen Zeilen kann man also per Python mit einem WFS kommunizieren. Voraussetzung dafür ist natürlich, dass man vorher das Capabilities-Dokument des Dienstes studiert und entsprechend den XML-Inhalt des POST-Requests auf den Dienst anpasst.

Appropos POST: Es wird im Internet manchmal angegeben, dass der POST-Parameter (in unserem Fall also die Variable xmlrequest) zuerst noch mit dem Modul urllib2 codiert werden müsse – etwa so:

enc_params = urllib2.quote(params)
req =  urllib2.Request("http://gdz.bkg.bund.de/wfs_altimeter__GUID",enc_params)

Dies ist jedoch nicht nötig, bzw. produziert einen Fehler beim Absetzen des Requests. Auch muss wohl nicht explizit die HTTP-Methode des Zugriffs angegeben werden (GET oder POST). Eine entsprechende Zeile würde so aussehen:

req.get_method() == 'POST'