Postpass – eine öffentlich nutzbare OSM-PostGIS

Hallo,

es kommt ja regelmäßig vor, dass hier irgendjemand fragt „wie kann ich dieses und jenes Feature aus OSM laden“, und dann gibt es oftmals hilfreiche Antworten mit Overpass oder sogar einem fertigen Overpass Turbo-Link.

Für manche Sachen ist Overpass die optimale Lösung. Aber manchmal denke ich auch, mensch, mit einem PostGIS-Query ginge das einfacher/besser. Nur hilft es den Leuten nicht so, wenn man schreibt „lade Dir das Extrakt runter, installiere PostGIS, nimm osm2pgsql, importiere die Daten, und dann kannst Du …“

Deshalb habe ich einen öffentlichen Dienst gebastelt, mit dem man – genau wie mit Overpass auch – die OSM-Daten abfragen kann, aber basierend auf einer PostGIS-Datenbank. Deswegen heisst das Ding auch “Postpass” :wink: In einem ersten Versuch hatte ich das mit dem Standard-OSM-Carto-Datenbankschema gemacht, aber in einer kleinen Runde am OSM-Samstag auf der FOSSGIS sind wir dann übereingekommen, dass man das Schema etwas verschlanken sollte. Es ist aber immer noch ein Datenschema mit osm2pgsql-Geometrien – das heisst, man schaut nicht (wie bei Overpass) auf die OSM-Rohdaten, sondern auf fertig zusammengebaute Geometrien. Die Ausgabe ist daher auch GeoJSON und nicht OSM-XML.

So kann man zum Beispiel Fast-Food-Restaurants in Karlsruhe finden:

curl -g http://postpass.geofabrik.de/api/0.2/interpreter --data-urlencode "data=
   SELECT tags, geom 
   FROM postpass_point
   WHERE tags->>'amenity'='fast_food' 
   AND geom && st_setsrid(st_makebox2d(st_makepoint(8.34,48.97),
      st_makepoint(8.46,49.03)), 4326)"

Das mit dem curl und der Boundingbox ist natürlich etwas nervig, daher kann man das ganze auch direkt in Overpass Turbo mit dem gewohnten {{bbox}} nutzen, man muss nur eine magische Zeile voranstellen, die dem Overpass Turbo sagt, dass es meinen Server ansprechen soll:

{{data:sql,server=https://postpass.geofabrik.de/api/0.2/}}
SELECT tags, geom
FROM postpass_point
WHERE tags->>'amenity'='fast_food'
AND geom && {{bbox}}

Das sind jetzt nur ganz einfache Beispiele, mit dem SQL kann man natürlich beliebig komplizierte Sachen machen.

Es gibt die Tabellen postpass_point, postpass_line, postpass_polygon, aber auch kombinierte Views namens postpass_pointpolygon und so weiter, wenn man (was bei POIs ja oft der Fall ist) Punkte und Polygone kombinieren will.

Wichtig ist, dass Queries immer eine Geometrie zurückgeben müssen, sonst kann kein GeoJSON konstruiert werden. Aber man kann beliebige PostGIS-Geometrieoperationen nutzen, zum Beispiel hier ein Buffer von 0,01 Grad um Deutschland herum

{{data:sql,server=https://postpass.geofabrik.de/api/0.2/}}
SELECT st_buffer(geom,0.01)
FROM postpass_polygon
WHERE tags->>'boundary'='administrative' 
AND tags->>'admin_level'='2'
AND tags->>'name'='Deutschland'

oder wenn man es wirklich exakt mit 1000 Metern berechnen will, durch eine kleine Reprojektions-Orgie:

{{data:sql,server=https://postpass.geofabrik.de/api/0.2/}}
SELECT st_buffer(geom::geography, 1000)::geometry
FROM postpass_polygon
WHERE tags->>'boundary'='administrative' 
AND tags->>'admin_level'='2'
AND tags->>'name'='Deutschland'

Solche Queries sind dann nicht mehr superschnell, aber sie gehen. Hier ist ein Query, der in ganz Deutschland alle Punkte findet, die ein addr:postal_code haben, der nicht zum umgebenden PLZ-Polygon passt:

SELECT
   p.osm_id, p.geom,
   p.tags->>'addr:postcode' as punkt_plz,
   plz.tags->>'postal_code' as poly_plz
FROM postpass_point p, postpass_polygon plz
WHERE plz.geom && st_setsrid(st_makebox2d(
   st_makepoint(5.53,47.23), st_makepoint(15.38,54.96)), 4326)
AND st_contains(plz.geom, p.geom)
AND p.tags->>'addr:postcode' <> plz.tags->>'postal_code'
AND plz.tags→>'boundary'='postal_code'

Auch Schnitt-Geometrien oder Vereiningungs-Geometrien kann man ausrechnen und zurückgeben lassen.

Wenn man mit einem speziellen Flag sagt, dass man keine Geometrie will, kann man auch SQL-Abfragen stellen, die z.B. irgendwas zählen, so zum Beispiel „was gibt es in Karlsruhe für verschiedene amenity-Werte“

curl -g https://postpass.geofabrik.de/api/0.2/interpreter \
   --data-urlencode "options[geojson]=false" \
   --data-urlencode "data=
      SELECT count(*), tags->>'amenity' as amenity
      FROM postpass_point
      WHERE tags?'amenity'
      AND geom && st_setsrid(st_makebox2d(
         st_makepoint(8.34,48.97),
         st_makepoint(8.46,49.03)), 4326)
      GROUP BY amenity"

oder „wieviele Meter von den verschiedenen Highway-Klassen gibt es in Karlsruhe“

curl -g https://postpass.geofabrik.de/api/0.2/interpreter \
   --data-urlencode "options[geojson]=false" \
   --data-urlencode "data=
      SELECT count(*), tags->>'amenity' as amenity
      FROM postpass_point
      WHERE tags?'amenity'
      AND geom && st_setsrid(st_makebox2d(
         st_makepoint(8.34,48.97),
         st_makepoint(8.46,49.03)), 4326)
      GROUP BY amenity"

Ihr könnt sogar für solche nicht-Geometrie-Abfragen Overpass Turbo benutzen, indem ihr der magischen {{data:sql}}-Zeile noch ein geojson=false hinzufügt:

{{data:sql,server=https://postpass.geofabrik.de/api/0.2/,geojson=false}}
SELECT count(*), tags->>'amenity' as amenity
FROM postpass_point
WHERE tags?'amenity'
AND geom && st_setsrid(st_makebox2d(
st_makepoint(8.34,48.97),
st_makepoint(8.46,49.03)), 4326)
GROUP BY amenity

Es gibt aber auch viele Sachen, die mit Overpass besser oder schneller gehen – insbesondere dann, wenn man stärker and den OSM-Daten arbeiten muss. Das „Postpass“ kann auch keine history.

Es gibt zwei GitHub-Repositories, in denen dieses Projekt beheimatet ist. Einmal GitHub - woodpeck/postpass: a simple API wrapper around PostGIS für die Software an sich (die kann prinzipiell mit jeder Art PostGIS-Datenbank eingesetzt werden) und einmal GitHub - woodpeck/postpass-ops: operational issues about the Postpass instance run on postpass.geofabrik.de für die konkrete Instanz, die ihr unter postpass.geofabrik.de erreicht. Dort habe ich auch eine Beispielsammlung angefangen, aber ich freue mich auch über Beispiele hier im Thread, oder wenn jemand eine Wikiseite im OSM-Wiki dazu machen will (das habe ich noch gar nicht angefangen).

Das ist jetzt alles noch ganz neu und bestimmt nicht perfekt. Was vorallem noch fehlt, ist eine Möglichkeit, von außen zu sehen, wie beschäftigt das System gerade ist. Das System arbeitet mit drei Warteschlangen für schnelle, mittlere, und langsame Requests. Wenn ihr einen Request schickt, der 5 Minuten rechnet, dann wird der auch bearbeitet, aber erst nachdem alle anderen gerade wartenden lang-dauernden Request bearbeitet sind; ein knackiger kleiner Request kann sich über eine der anderen Warteschlangen daran vorbeimogeln und wird schneller beantwortet.

Spielt doch mal ein wenig damit herum. Ich freue mich über Feedback. Besonders freue ich mich, dass Martin den Support so schnell in Overpass Turbo eingebaut hat, das macht die Sache wirklich viel bequemer zu nutzen :wink:

Meinen FOSSGIS-Vortrag dazu könnt ihr hier anschauen: Overpass Turbo goes PostGIS - media.ccc.de – allerdings sind die Beispiele darin noch für die erste Version der Datenbank mit den Tabellen planet_osm_point und so weiter.

Bye
Frederik

16 Likes

Seit Anfang des Jahres gibt es ja die Landmasse von Deutschland nicht mehr in den OSM Daten. Ich tue mich recht schwer damit, die aus den vorhandenen Daten zu rekonstruieren, aber @Nakaner meinte, dass das mit einer Postgres-Datenbank kein Problem sei. Kann man hier ein Query basteln, der das macht?

Hihi, da hat sich der Kollege Nakaner ein bisschen weit aus dem Fenster gelehnt. Es geht dann einfach, wenn man über vorprozessierte Landflächen (von osmdata.openstreetmap.de) verfügt. Wären die in der Datenbank, könnte man die Schnittfläche der Deutschlandrelation mit diesen Flächen bestimmen. Aber die sind (derzeit) nicht in der Datenbank - die einzelnen Küstenlinien schon, aber eben nicht die daraus berechnete Landmasse. Das macht das osm2pgsql nicht automatisch.

Es gibt aber einen etwas schmutzigen Trick, und zwar: In Deutschland enthalten die Bundesländer noch die Hoheitsgewässer, die Kreise aber nicht mehr. Also: “Bilde die gemeinsame Fläche aller Kreise, die in Deutschland liegen, und dann wirf halt noch Berlin und Hamburg dazu”:

{{data:sql,server=https://postpass.geofabrik.de/api/0.2/}}
SELECT st_union(kreise.geom)
FROM postpass_polygon kreise, postpass_polygon deutschland
WHERE st_contains(deutschland.geom, kreise.geom)
AND kreise.tags->>'boundary'='administrative'
AND (kreise.tags->>'admin_level'='6'
    OR kreise.tags->>'name' in ('Hamburg','Berlin'))
AND deutschland.osm_id=51477 and deutschland.osm_type='R'

Hier habe ich ein bisschen Zeit gespart und das Deutschlandpolygon anhand seiner (mir bekannten) Relations-ID ausgewählt, aber man hätte auch sagen können “finde eine Admingrenze namens Deutschland”, dann wäre es von der ID unabhängig.

1 Like

Gab es dazu eine Diskussion?

1 Like

Wär super, wenn man darüber in einem separaten Thread sprechen könnte. “Wie kann ich mit Postpass die Geometrie von X laden” ist on-topic, “warum ist die Geometrie von X nicht in OSM” ist off-topic :wink:

5 Likes

Fair wäre es gewesen, zu mindest auf die jüngste Disskussion zu verlinken. Da du das anscheinend nicht möchtest, hole ich es extra für dich nach… :frowning:

Sven

2 Likes

@woodpeck

Warum lädst du nicht die Landflächen- und Meeresflächenpolygone von osmdata.openstreetmap.de in separate Datenbanktabellen (+ Systemd-Timer, um sie regelmäßig zu aktualisieren)?

1 Like

Grundsätzlich ist die Bereitstellung des Services sehr zu begrüßen. Um den Service zu nutzen muss man allerdings sowohl SQL als auch die PostGIS-Erweiterungen beherrschen (und sicher anwenden können).

Das sind aus meiner Sicht noch höhere Hürden als beim grep-ähnlichen Overpass.

Was grundsätzlich fehlt, sowohl bei Overpass als auch bei Postpass, ist eine KI-Funktionalität, die aus einer sprachlich formulierten Beschreibung eine entsprechende Overpass- oder Postpass-Abfrage generiert. Vielleicht ein interessantes Thema für eine Diplomarbeit.

PS: Möglicherweise kommt man durch Trainieren eines bestehenden KI-Modell mit vielen Beispielen schon recht weit.

Probiere doch einmal Perplexity aus. Das ist eine Kombination aus KI und Suchmaschine, bei der die KI Suchaufträge auslösen und die Suchergebnisse verarbeiten kann.

Die folgende Anfrage funktioniert für mich schon erstaunlich gut:

Schau Dir die Beispiele für Postpass in https://community.openstreetmap.org/t/postpass-eine-offentlich-nutzbare-osm-postgis/128283 an. Bitte erstelle eine SQL-Abfrage für Postpass, um ‹hier einfügen, wonach Du suchst›. Postpass-Abfragen dürfen kein Semikolon am Ende enthalten.

Wir sollten (sowieso) ein paar Anfrage-Beispiele auf einer Wiki-Seite sammeln. Wenn man Perplexity darauf verweisen kann, dann wären die Ergebnisse bestimmt noch besser.

1 Like

Als jemand, der sich seit kurzem in SQL/PostGIS einarbeitet, habe ich mich sehr über Frederiks Initiative gefreut. Endlich können wir einer obskuren Abfragesprache (Overpass) noch eine weitere (SQL) daneben stellen :slight_smile:

Mein Eindruck ist, dass die Nutzbarkeit von Postpass für die Hobby- oder Gelegenheits-Anwenderin stark davon profitieren könnte, wenn sich die Sexyness einiger häufig genutzter Ausdrücke und Funktionen noch steigern würde. Der neuen Datenbankstruktur geschuldet ist z.B. die etwas gewöhnungsbedürftige tags->>-Schreibweise. Ließe sich vielleicht bei der Interpretation der Abfrage eine Art Parsing einbauen, um “unbekannte” Spaltenbezeichner – also eigentlich alles außer geom, tags und ggf. selbst deklarierte Begriffe – automatisch in die tags->>-Schreibweise umzuwandeln? Sodass eine Abfrage in der folgenden Form möglich wird (wobei highway und oneway jeweils in tags->>'highway'/tags->>'oneway' umgewandelt werden würden):

SELECT geom, highway
FROM postpass_line
WHERE highway IS NOT NULL
AND oneway IN ('yes', '-1')

Oder die Behandlung von – in unserem Universum essentiellen – Gebietsangaben wie “finde Objekte in…”. Du hast ja in deinem Vortrag bzw. der Session auf dem OSM-Samstag erwähnt, dass du ein Repo für eigene Funktionen planst – vielleicht ist das ein Ort dafür. Sowas wie eine FunktionPP_IsIn(geom, "Karlsruhe") oder so, statt sich die Bounding Boxen oder admin-Tags immer erst selbst zusammenbauen zu müssen.

Auch dem Umgang mit (vermeintlich) numerischen Werten könnte man sich dann widmen. Vergleiche wie width < 0.5 oder step_count > 100 scheinen gerade noch etwas umständlich zu sein, weil man sich den Wert nicht nur selbst in ein passendes Format casten müsste, sondern bestimmt auch noch mit Zeichenketten und anderen kreativen, nicht numerischen Values umgehen müsste…?

Und was ich mich auch gefragt habe: Wenn ich gar kein Interesse an der Geometrie habe, muss die Rückgabe dann zwingend in einem json-Format sein? Wenn ich z.B. “mal schnell” die Summe der Länge aller Straßen [kategorisiert nach Straßenklasse o.ä.] an einem Ort wissen will, fände ich ein “normales”, tabellenähnliches SQL-Ausgabeformat oder so vielleicht übersichtlicher.

Soweit mal die (jaja, bestimmt naive) Sichtweise eines Menschen, der als Kind nicht in den Zaubertrank gefallen ist.

P.S. @woodpeck Bei deinem Beispiel „wieviele Meter von den verschiedenen Highway-Klassen gibt es in Karlsruhe“ hast du ausversehen nochmal das vorherige amenity-Beispiel reingepastet.

Um eine KI geeignet zu trainieren sollte m.E. folgendes bereit gestellt werden:

  • Spezifikation der Sprache (SQL, PostGIS).
  • Alle Features / Funktionen der Sprache.
  • Alle DB-Schema der konkreten Implementierung.
  • Viele Beispiele nach dem Muster:
    • Detaillierte Fragestellung.
    • Konkrete Abfrage.
    • Erzieltes Ergebnis.

Danke für das Feedback. Den Fehler im Beispiel korrigiere ich.

Grundsätzlich habe ich mit meiner Initiative eigentlich nicht das Ziel verfolgt “baue irgendwas, was es Leuten leicht macht, irgendwie an OSM-Daten zu kommen”. Mein Ziel war, dass ich den Leuten die Augen dafür öffnen wollte, wie schnell und cool manche Sachen mit einer PostGIS-Abfrage machbar sind, und dass einige dann, wenn sie sich an ein größeres Projekt machen, vielleicht doch mal schnell ihre eigene PostGIS aufsetzen, weil sie schon wissen, dass das gut geht.

Jeder, der OSM-Daten für einen bestimmten Anwendungszweck verarbeitet, wird sich vermutlich eigene Konvertierungsregeln schreiben - numerische Tags in echte Zahlen umrechnen (vielleicht sogar ein “maxspeed=RO:urban” in eine Zahl wandeln) und so weiter. Solche Konvertierungen würden dann idealerweise bereits beim Import durchgeführt; ebenso würde man beim Import auch alle Informationen wegwerfen, die einen nicht interessieren. Das hier soll erstmal eine “Allzweck”- oder wenigstens “Vielzweck”-Datenbank sein, deswegen wollte ich darauf verzichten, allerlei Speziallogik beim Datenimport einzubauen.

Aber so Hilfsfunktionen wie das, was Du skizziert hast - ob ein Bool-Wert true ist oder nicht, oder eine Umwandlung in eine Zahl oder so - das könnte man natürlich alles einbauen.

Das Ersetzen von irgendwas im Query durch irgendwas anderes wäre nur möglich, wenn ich den SQL-Query in meinem Programm parse. Das habe ich bislang vermieden; SQL-Queries können sehr lang und komplex werden und ich fürchte, dass “ersetze mal eben ein unbekanntes dings durch tags->>‘irgendwas’” ziemlich schiefgehen kann, wenn man nicht wirklich alle Eventualitäten beachtet.

Auch beim Lesen von Daten aus der Datenbank ist mein Programm total primitiv - es macht keinen Loop über mehrere Zeilen oder fragt ab, wieviele Spalten mit was für Datentypen zurückgeliefert werden; es gibt einfach nur den Wert zurück, der in Zeile 1, Spalte 1 des Ergebnisses steht, alles andere passiert in der Postgres. Daher der JSON-Zwang. Das könnte man auch im Programm verbessern - aber ich fürchte, dass das auch ein Fass ohne Boden sein könnte (“hey könnte man nicht noch einen Parameter einbauen mit dem man sagen kann in welchem Datumsformat eine Timestamp-Spalte ausgegeben wird…”), und wenn ich die Benutzer dazu kriegen kann, sich das Ergebnis einfach selbst mit einem Tool wie “jq” zu formatieren, dann würde ich mir den Aufwand sparen :wink: