Neben der Bounding-Box gibt es weitere, dem Zielgebiet besser anpassbare Begrenzungsrahmen.
Koordinaten in Breiten- und Längengrad sind zwar als Konzept gut verständlich, aber die wenigsten Menschen kennen zu den sie interessierenden Orten die Koordinaten auswendig.
Es wird daher zunächst die indirekte Suche anhand von benannten Objekten vorgestellt. Die Suche in Flächen ist dabei einerseits herausragend häufig, hat aber andererseits mehrere Besonderheiten. Sie wird daher in einem anderen Unterkapitel behandelt.
Im ersten Unterkapitel geht es um die Suche im Umkreis von benannten Objekten. Die Suche im Umkreis von Koordinaten schließt sich an. Zuletzt werden noch Polygone als räumlicher Suchfilter vorgestellt.
Es ist eine anspruchsvolle Aufgabe, aus einem Text zuverlässig einen konkreten Ort zu ermitteln. Daher fällt dies auch eigentlich einem Geocoder zu, z.B. Nominatim, und wird hier nicht vertieft. Mit den Ergebnissen von Nominatim kann schon die im nächsten Abschnitt beschriebene Suche um Koordinaten benutzt werden.
Es gibt aber genug Beispiele, bei denen bereits der Name das richtige Objekt liefert:
nwr[name="Kölner Dom"]; out geom;
In Zeile 1 suchen wir nach allen Objekten,
die ein Tag name
mit Wert Kölner Dom
besitzen.
Dieses wird im Set _
abgelegt,
und in Zeile 2 gibt out geom
aus, was es im Set _
vorfindet.
Zur Erinnerung: Die Lupe zoomt auf die Fundstellen heran. Gerade bei indirekten Filtern ist es oft sinnvoll, die ursprüngliche Objektsuche auszuführen, um auszuschließen, dass es weitere gleichnamige Objekte an anderen Orten gibt:
nwr[name="Viktualienmarkt"]; out geom;
Eine Bounding-Box oder die Angabe einer umschließenden Fläche können helfen:
area[name="München"]; nwr(area)[name="Viktualienmarkt"]; out geom;
Das bzw. die gewünschten Objekte stehen hier nach Zeile 2 im Set _
.
Wir könnten nun alle Objekte im Umkreis von 100 Metern um den Kölner Dom finden:
nwr[name="Kölner Dom"]; nwr(around:100); out geom;
Allerdings warnt Overpass Turbo zurecht vor der Größe der zurückkommenden Datenmenge. Es erschließt sich auch nicht unmittelbar, warum eigentlich Gleise zwischen Paris und Brüssel als in der Nähe des Kölner Doms gelten sollen. Das Problem sind daher einmal mehr räumlich ausgedehnte Relations. Da dies beim Viktualienmarkt wegen Fernwander- und Radwegen kaum besser ist ...
area[name="München"]; nwr(area)[name="Viktualienmarkt"]; nwr(around:100); out geom;
... lässt sich vermuten, dass es sich um ein häufiges Problem handelt. Dies setzt der Nutzbarkeit des Around-Filters ohne weitere Filter enge Grenzen.
Auf der technischen Ebene haben wir wieder unsere benannten Objekte vor Zeile 3 im Set _
.
Das Statement Around filtert nun aus allen Objekten nur diejenigen heraus,
die zu mindestens einem Objekt im Set _
einen Abstand von höchstens dem angegebenen Wert 100
in Metern haben.
Der Mechanismus zur Verkettung hat ein eigenes Unterkapitel, und Sets sind in der Einleitung eingeführt worden. Das Beispiel dort vom Anfang zeigt eine Anwendung der Around-Filters, die hilfreich ist, da sie den Filter mit einem Filter nach einem Tag kombiniert. Werkzeuge gegen übergroße Datenmengen sind im Unterkapiel Geometrien diskutiert worden.
Eine weitere mögliche Lösung, um den obigen Fall zumindest sinnvoll anzeigen zu können, wäre nach Ways statt nach allen Objekten zu filtern und nur die Relations zu ermitteln, die die gefundenen Ways referenzieren; für den Kölner Dom:
nwr[name="Kölner Dom"]; way(around:100); out geom; rel(bw); out;
Zeile 1 bringt die benannten Objekte ins Set _
;
Zeile 2 findet alle Ways,
die zu mindestens einem der Objekte aus dem Set _
höchstens 100 Meter Abstand haben;
das Ergebnis ersetzt den Inhalt von Set _
.
Zeile 3 gibt den Inhalt von Set _
aus, also die in Zeile 2 gefundenen Ways.
Zeile 4 findet alle Relations, die mindestens einen der im Set _
abgelegten Ways referenzieren
und ersetzt den Inhalt von _
durch dieses Ergebnis.
In Zeile 5 wird der Inhalt von Set _
, also die gefundenen Relations, ausgegeben,
aber im Gegensatz zu Zeile 3 werden keine Koordinaten mitgeliefert -
dies schrumpft die Relations auf eine handhabbare Größe.
Im Umkreis kann auch anhand von Koordinaten statt vorhandener Objekte gesucht werden. Ein Beispiel nahe Greenwich auf dem Nullmeridian:
nwr(around:100,51.477,0.0); out geom;
Es kommt ein Filter in Zeile 1 zum Einsatz:
es werden alle Objekte im Set _
abgelegt,
die höchstens 100 Meter Abstand zu der gegebenen Koordinate haben.
Zeile 2 gibt das Set _
aus.
Es gelten die gleichen Vorsichtshinweise wie bei allen anderen Volldaten-Suchen mit Relations: sehr schnell hat man sehr viele Daten. Die Reduktionstechniken von Bounding-Boxen und aus dem letzten Abschnitt greifen hier aber ebenfalls.
Es gibt aber keinen Zwang nach Relations zu suchen. Man kann auch nur nach Nodes, nur nach Ways ...
way(around:100,51.477,0.0); out geom;
... oder nach Nodes und Ways suchen:
( node(around:100,51.477,0.0); way(around:100,51.477,0.0); ); out geom;
Hier nutzen wir ein Union-Statement (wird später eingeführt),
um die Ergebnisse der Umkreissuche nach Nodes und der Umkreissuche nach Ways zusammenzuführen.
Zeile 2 und Zeile 3 filtern je einen Objekttyp anhand eines Around-Filters,
und Union fügt die Ergebnisse beider Query-Statements im Set _
zusammen.
Damit werden auch Umkreise mit einem Radius von 1000 Metern und mehr durchführbar.
Relationen kann man jetzt ähnlich wie oben ohne Geometrie wieder hinzunehmen:
( node(around:1000,51.477,0.0); way(around:1000,51.477,0.0); ); out geom; rel(<); out;
In Zeile 5 steht als Eingabe im Set _
noch das Ergebnis des Union-Statements zur Verfügung.
Der Filter (<)
lässt nur Objekte zu,
die auf mindestens ein Objekt in der Eingabe referenzieren -
das sind genau die Relationen, die einen Bezug zum Suchgebiet haben.
Um mit Suchen umzugehen, die nicht gut in Bounding-Boxen passen, stellen wir hier noch die Umkreissuche um einen Linienzug vor. Dazu definiert man einen Pfad über zwei oder mehr Koordinaten, und es werden alle Objekte gefunden, deren Abstand geringer als der angegebene Wert in Metern ist:
( node(around:100,51.477,0.0,51.46,-0.03); way(around:100,51.477,0.0,51.46,-0.03); ); out geom; rel(<); out;
Gegenüber der vorangehenden Abfrage haben sich nur die Zeilen 2 und 3 geändert; die Koordinaten werden jeweils mit Kommata getrennt aneinandergehängt.
Eine weitere Methode, um mit Suchgebieten umzugehen, die nur schlecht in Bounding-Boxen passen, ist die Suche anhand eines Polygons.
Zwar decken Areas bereits sehr viele Anwendungsfälle ab, indem sie die Suche in exakt einem benannten Gebiet erlauben. Aber wenn es darum geht, solche Gebiete etwas zu erweitern oder auch beliebige Freiformen zu schneiden, muss zwangsläufig die Flächenbegrenzung als explizites Polygon übergeben werden.
Zur Illustration zunächst eine Suche nur nach Nodes mit einem Dreieck als Grenze, um die Polygonform gut auf der Karte sehen zu können:
node(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); out geom;
In Zeile 1 suchen wir nach Nodes,
und der Filter (poly:...)
lässt nur solche Objekte zu,
die innerhalb des in den Anführungszeichen notierten Polygons liegen.
Das Polygon ist eine Liste von Koordinaten der Form Breitengrad-Längengrad,
wobei zwischen den Zahlwerten nur Leerzeichen liegen dürfen.
Nach der letzten Koordinate ergänzt Overpass API die schließende Kante.
Sehr viele Daten liefert wieder einmal die Suche nach allen drei Objektarten:
nwr(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); out geom;
Wie schon zuvor kann dies durch die beiden Schritte Nodes plus Ways und Rückwärtsauflösen der Relations eingehegt werden; die Datenersparnis entsteht nur dadurch, dass zu den Relations die Geometrie weggelassen wird:
( node(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); way(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); ); out geom; rel(<); out;
Können auch Löcher und mehrere Komponenten realisiert werden?
Mehrere Komponenten können per Union-Statement realisiert werden. Da Union-Statements beliebig viele Unterstatements haben können, können wir die Query-Statements für die Komponenten einfach hintereinander schreiben, hier gleich für die Nodes-und-Ways-Variante:
( node(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); way(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01"); node(poly:"51.491 -0.01 51.498 -0.03 51.505 -0.01"); way(poly:"51.491 -0.01 51.498 -0.03 51.505 -0.01"); ); out geom; rel(<); out;
Der Umriss wird hier jeweils zweimal angegeben; dies lässt sich im Moment leider nicht sinnvoll vermeiden.
Entsprechend könnte es für Löcher naheliegend sein, das Block-Statement Difference zu verwenden. Dann schneidet man allerdings auch Objekte weg, die teilweise im Loch und teilweise im umgebenden Polygon liegen, denn Difference würde diese Objekte im Loch ja finden.
Stattdessen funktioniert es, den zum ersten Punkt des Lochs nächstgelegenen Punkt der Außenlinie zu verdoppeln und den Linienzug mit gedoppeltem Start- und Endpunkt dazwischen einzufügen.
Wenn wir z.B. aus dem Dreieck 51.47 -0.01 51.477 0.01 51.484 -0.01
das Dreieck 51.483 -0.0093 51.471 -0.0093 51.477 0.008
ausschneiden wollen, dann
51.484 -0.01
,
erhalten also 51.47 -0.01 51.477 0.01 51.484 -0.01 51.484 -0.01
51.483 -0.0093
am Ende,
erhalten also fürs Loch 51.483 -0.0093 51.471 -0.0093 51.477 0.008 51.483 -0.0093
51.47 -0.01 51.477 0.01 51.484 -0.01 51.483 -0.0093 51.471 -0.0093 51.477 0.008 51.483 -0.0093 51.484 -0.01
Zur Illustration die fertige Abfrage für Nodes. Sie funktioniert auch für alle anderen Objekttypen und kann mit Union kombiniert werden, aber dann sieht man schlechter das tatsächlich durch das Polygon ausgewählte Gebiet:
node(poly:"51.47 -0.01 51.477 0.01 51.484 -0.01 51.483 -0.0093 51.471 -0.0093 51.477 0.008 51.483 -0.0093 51.484 -0.01"); out geom;
weiter: Suche per Area