Usage
Vocabulary
The Overpass API works by selecting elements from the OSM database and organising them into sets, on which further filtering and combinations can be done. A statement is a partcular operation which collects, filters or combines statements or sets or apply operations on sets.
Statements may or may not return a set and are represented by the generic Statement class.
The Set class represents a query statement that returns a set, and can be treated as the
resulting set of the statement.
Elements filtering
Query nodes, ways, relations and areas with specific filters:
nodes = Nodes(
bounding_box=(50.6,7.0,50.8,7.3), name="Foo", amenity="cinema")
print(build(nodes))
Outputed compiled query:
node(50.6,7.0,50.8,7.3)["name"="Foo"]["amenity"="cinema"];
The constructors of a query statement can receive various predefined filters, and an arbitrary number of tag equality filters. It is also possible to chain specific filters. Each chaining returns a new set which is intersected with the set the filter is applied on. This allows to reuse any intermediate set in later statements. During compilation, chained intersections are merged into a single element selection:
nodes = Nodes()
nodes = nodes.within((50.6,7.0,50.8,7.3))
nodes = nodes.where(name="Foo")
nodes = nodes.around(100.0, lats=[50.7], lons=[7.2])
node(50.6,7.0,50.8,7.3)["name"="Foo"](around:100.0,50.7,7.2);
This is not always possible however, for example if a filter requires an input set, the statement computing that set is executed before and its result is stored in a variable. For example:
city = Nodes(name="Paris", place="city")
nodes = Nodes().around(10000., city).where(amenity="cinema")
node["name"="Paris"]["place"="city"]->.set_0;
node(around.set_0:10000.0)["amenity"="cinema"];
To add an arbitrary number of filters, use the filter method, which takes a list
of Filter instances or strings as arguments:
paris = Areas(name="Paris")
nodes = Nodes().filter(
Area(paris), Key("amenity"), Changed(datetime(2017, 11, 19)))
area["name"="Paris"]->.set_0;
node(area.set_0)["amenity"](changed:"2017-11-19T00:00:00Z");
Available filters are listed in the API Reference. In the case of area sets, it is straightfoward to obtain the elements within the areas via specific methods. The above query can also be written:
Areas(name="Paris").nodes_within(Key("amenity"), Changed(datetime(2017, 11, 19))
To query any elements (nodes, ways and relations), use the base Elements class:
elements = Elements(name="Foo")
nwr["name"="Foo"];
Unions, differences and intersections
The Union and Difference statements allow to build a union of any number of sets
and a difference between two sets.
union = Union(
Nodes(name="Foo"), Ways(name="Foo"), Relations(name="Foo"))
print(build(union))
diff = Difference(
Nodes(bounding_box=(50.6,7.0,50.8,7.3)), Nodes(name="Foo"))
print(build(diff))
(node["name"="Foo"]; way["name"="Foo"]; rel["name"="Foo"];);
(node(50.6,7.0,50.8,7.3); - node["name"="Foo"];);
It is also possible to use addition and substraction operators to create unions and differences:
union = Nodes(name="Foo") + Ways(name="Foo") + Relations(name="Foo")
print(build(union))
diff = Nodes(bounding_box=(50.6,7.0,50.8,7.3)) - Nodes(name="Foo")
print(build(diff))
(node["name"="Foo"]; way["name"="Foo"]; rel["name"="Foo"];);
(node(50.6,7.0,50.8,7.3); - node["name"="Foo"];);
Since unions and differences are sets, it is possible to further apply filters on them:
union = Nodes(name="Foo") + Ways(name="Foo")
union = union.where(amenity="cinema")
(node["name"="Foo"]; way["name"="Foo"];)->.set_0;
nwr.set_0["amenity"="cinema"];
Intersections are built via a specific Intersect filter, and can be called from a query
statement:
union = Nodes(name="Foo") + Ways(name="Foo")
nodes = Nodes().intersection(union).within((50.6,7.0,50.8,7.3))
print(build(nodes))
(node["name"="Foo"]; way["name"="Foo"];)->.set_0;
node.set_0(50.6,7.0,50.8,7.3);
Or can be constructed using the multiplication operator:
itsc1 = Nodes(name="Foo") * Nodes(amenity="cinema")
print(build(itsc1))
itsc2 = Nodes(name="Foo") * Ways(name="Foo")
print(build(itsc2))
node["name"="Foo"]["amenity"="cinema"];
way["name"="Foo"]->.set_0;
node["name"="Foo"].set_0;
Recursed down and up sets
It is possible to retrieve all related elements to the elements of a given set by recursing over it. Recursing up returns the set of elements which contain elements from the input set. Recusring down returns the elements that are part of the input set’s elements. Recursing down or up relations also includes indirectly related elements. For more details, check the official Overpass QL documentation.
a = Nodes(name="Foo", label="a")
union = a + a.recursed_down()
union.out()
print(build(union))
node["name"="Foo"]->.a;
(.a; .a >;);
out;
Labelled sets
Each statement can receive a label argument. This label will be used as the name of
variable (if no conflict) in which the result set of the statement (if any and required
at compile time) will be stored.
city = Areas(name="London", label="london")
bars = Nodes(name="bar", within=city)
area["name"="London"]->.london;
node(area.london)["name"="bar"];
Raw statements
It is possible define custom Overpass QL strings as statements via a RawStatement.
Raw statements allow to treat a string as a part of a larger query which depends or
is depended by other statements using placeholders.
area = Areas(name="Berlin")
raw = RawStatement("node(area.{a})[!opening_hours]->.{:out_var};", a=area)
raw.out()
area["name"="Berlin"]->.set_0;
node(area.set_0)[!opening_hours]->._;
out;
The {:out_var} placheolders is a special placheolder indicating where to insert
the name of the output variable of this statement (if it needs one).