Overpass API/Overpass API by Example
Servers status · Versions · Development · Technical design · Installation · XAPI compatibility layer · Public transport sketch lines · Applications · Source code and issuesOverpass turbo · Wizard · Overpass turbo shortcuts · MapCSS stylesheets · Export to GeoJSON · more · Development · Source code and issues · Web siteOverpass Ultra · Examples · Overpass Ultra extensions · MapLibre stylesheets · URL Params · more · Source code and issues · Web site
The Overpass API offers a variety of search possibilities. This is also known as querying. The results of those searches or queries can be displayed directly on a map, but it is also possible to retrieve only the data. On this page we will focus on examples that have to be displayed on a map. This means that all queries can be inserted in the code editor of Overpass turbo. From there the query can be executed and the result will be shown on a map.
Simply click on the Icon next to each query to try it in overpass turbo!
Introduction
The simplest way to get started with Overpass API is to use Taginfo and to navigate to the tag you are interested in. Take post boxes as an example. Push the Overpass Turbo button there in the upper right. This will start the Overpass Turbo user interface and produce the following query
/*
This query looks for nodes, ways and relations
with the given key/value combination.
Choose your region and hit the Run button above!
*/
[out:json][timeout:25];
// gather results
(
// query part for: “amenity=post_box”
node["amenity"="post_box"]({{bbox}});
way["amenity"="post_box"]({{bbox}});
relation["amenity"="post_box"]({{bbox}});
);
// print results
out body;
>;
out skel qt;
The Overpass Turbo user interface has a lot of capabilities. For the moment it should suffice that you have in the upper right corner of the map view a textfield. Type in your favorite location. It will then, with the use of Nominatim move the map view to that location. Type in e.g. Manhattan and choose the first hit. The map then jumps to the upper end of the Central Park in Manhattan. Zoom out two levels to see some of the built area. Run the query by hitting Run.
The Taginfo Example
Now we can start to understand the query.
Most of the previous query are comments - everything after // and between /* and */. Hence, a more concise variant of the query is
[out:json][timeout:25];
(
node["amenity"="post_box"]({{bbox}});
way["amenity"="post_box"]({{bbox}});
relation["amenity"="post_box"]({{bbox}});
);
out body;
>;
out skel qt;
As we are about a simple example here, we reduce the query further in two steps to get it really simple.
We know for post boxes by common knowledge that they are always nodes:
[out:json][timeout:25];
(
node["amenity"="post_box"]({{bbox}});
);
out body;
>;
out skel qt;
And we can remove most of the cryptic syntax that mostly tells what to do with ways and relations. We can get the same result as before with only two lines.
node["amenity"="post_box"]({{bbox}});
out;
All these steps work with a tag from Taginfo. There is no magic involved. It just replaces amenity = post_box with the respective value.
Some Standard Features
Now what about the extra syntax? Let us review them bit by bit. To see an effect, we switch to query for restaurants instead of post boxes, because then you can see the effect in adding support for ways.
node["amenity"="restaurant"]({{bbox}});
out;
Not all restaurants are mapped as nodes. Hence we want to search for ways and relations too:
way["amenity"="restaurant"]({{bbox}});
out;
Running this query will result in a message that geometry is missing. This is because the OpenStreetMap data model does not directly associate coordinates to ways and relations. Ways and relations instead get their geometry by resolving their node references.
The out statment has various modes to address this problem in a more convenient way: You can choose for example out geom to let Overpass API resolve the coordinates for you. Or you use out center to collapse the geometry to a single coordinate per object:
way["amenity"="restaurant"]({{bbox}});
out center;
We can put all these requests simply one after another. Then we get the results one after another and Overpass Turbo displays them all at once.
node["amenity"="restaurant"]({{bbox}});
out;
way["amenity"="restaurant"]({{bbox}});
out center;
relation["amenity"="restaurant"]({{bbox}});
out center;
The Taginfo generated queries use instead the sequence out body, >, out skel qt. This is merely for historical reasons. This does essentially the same as out geom but delivers elements strictly from the OpenStreetMap data model. Overpass Turbo nowadays can directly process the results of out geom and out center and the delivered raw data is more concise.
As a last step, we make the query more concise:
[bbox:{{bbox}}];
( node["amenity"="restaurant"];
way["amenity"="restaurant"];
relation["amenity"="restaurant"]; );
out center;
This still has the same result as before. But we have brought the bounding box to a single place in the beginning of the query. An important reason to do so is that you should develop the habit of having always a bounding box unless you really want results from all over the planet.
We also have grouped the query statements by an union statement - these are the parentheses. This allows to have only a single output statement.
The Taginfo generated query also uses two other global settings: The first is [out:json]. This might be an advantage if you want to directly read the returned data. You can do so on the Data tab over the map view.
The second one is [timeout:25]. This limits the runtime of the query to 25 seconds. If something goes wrong then the limit assures that neither the server nor you waste time. Like the Global Bounding Box it is a useful precaution.
In total, we arrive at the following request:
[timeout:25][bbox:{{bbox}}];
( node["amenity"="restaurant"];
way["amenity"="restaurant"];
relation["amenity"="restaurant"]; );
out center;
Newer Overpass API versions also allow to combine node
, way
and relation
using the shortcut nwr
:
[timeout:25][bbox:{{bbox}}];
nwr["amenity"="restaurant"];
out center;
Rare Tags
If you do not know beforehand where you would expect a tag, but if you are pretty sure that there are not much more than some hundred elements, then you can drop the bounding box.
[timeout:25];
( node[name="Belsenplatz"];
way[name="Belsenplatz"];
rel[name="Belsenplatz"]; );
out center;
If you also leave the timeout setting out, then the server sets a default of 180 seconds:
( node[name="Belsenplatz"];
way[name="Belsenplatz"];
rel[name="Belsenplatz"]; );
out center;
Other examples
In addition to this page, there are lots more examples are in the Overpass API dev blog. There are also links to specific examples from sections below.
Understanding Data
This section is about queries that answer the question whether specific data situations exist.
There is no sharp boundary to the Building Blocks section below. The general criterion is that the examples here are from a geographic point of view, but the Building Blocks are from a database point of view and may be purely technical criteria.
The difference to Quality Assurance is that the queries here usually have valid results, whereas results of Quality Assurance queries usually need to be fixed one way or another.
Tagging
To make sense, an OpenStreetMap object always needs to have a type. Usually, humans need in addtion a handle to refer to such an object - this is what is called a name. With respect to KISS, this handle is almost always in the name tag. But sometimes there is a valid reason to have it in another tag like ref or name:fr.
One of the standard assumptions when dealing with OpenStreetMap data is that named objects have a type. This is opposed to unnamed elements of the OpenStreetMap database. They may be part of another object and then do not constitute an object on their own.
Conversely, at least certain types of objects always have names - there is no unnamed museum. However, is there no unnamed bus service? Has every restaurant a name?
Tagged Nodes
The following query returns all nodes in the current bbox having at least one tag:
node({{bbox}})(if:count_tags() > 0);
out geom;
The question was posed here.
Nodes with exactly one specific tag
The aim of the following query is to find out all nodes with a specific tag and no other tag.
Since 0.7.54 you can use a filter expression:
node({{bbox}})[name](if:count_tags() == 1);
out geom;
Please replace the name with whatever tag may fit your needs. The pitfall of this approach is that you will not find objects that have a meaningless second tag. If you want to do so then you should filter out all acceptable tags. A simple example is in the Building Blocks. More examples are in the Overpass API dev blog.
The original question comes from this source.
Leading & Trailing spaces in names
Find name* tag values with leading and trailing space characters, or multiple consecutive spaces.
[bbox:{{bbox}}];
(
node[~"^name"~"^[[:blank:]]|[[:blank:]]$|[[:blank:]]{2,}"];
way[~"^name"~"^[[:blank:]]|[[:blank:]]$|[[:blank:]]{2,}"];
);
(._;>;);
out meta;
Count Pharmacies per County (updated 0.7.54)
The following example returns the number of pharmacies per county with regional key starting with "057". The output is in CSV format.
/*
CSV-Format: To load the query results in LibreOffice, click on
"Export" -> "Open Raw Data directly from Overpass API"
in overpass turbo. Fields are tab-separated.
*/
// Define fields for csv output
[out:csv(::type, "de:regionalschluessel", name,
::count, ::"count:nodes", ::"count:ways", ::"count:relations")];
//All areas with regional key (German: "Regionalschlüssel") starting with 057
area["de:regionalschluessel"~"^057"];
// Count the pharmacies in each area
foreach->.regio(
// display details for the current area
.regio out;
// Collect all Nodes, Ways and Relations with amenity=pharmacy in the current area
( node(area.regio)[amenity=pharmacy];
way(area.regio)[amenity=pharmacy];
rel(area.regio)[amenity=pharmacy];);
// Count the elements in the current area Area
out count;
);
Since 0.7.54 we can merge the two separate lines for the area and statistical counts into a single line:
[out:csv( "de:regionalschluessel", name, total, nodes, ways, relations )];
//All areas with regional key (German: "Regionalschlüssel") starting with 057
area["de:regionalschluessel"~"^057.*"];
// Count the pharmacies in each area
foreach->.regio(
// Collect all Nodes, Ways and Relations wth amenity=pharmacy in the current area
( node(area.regio)[amenity=pharmacy];
way(area.regio)[amenity=pharmacy];
rel(area.regio)[amenity=pharmacy];);
make count "de:regionalschluessel" = regio.set(t[ "de:regionalschluessel"]),
name = regio.set(t["name"]),
total = count(nodes) + count(ways) + count(relations),
nodes = count(nodes),
ways = count(ways),
relations = count(relations);
out;
);
Count number of wikimedia_commons globally
[out:csv(::count, ::"count:nodes", ::"count:ways", ::"count:relations")][timeout:125];
(
node["wikimedia_commons"];
way["wikimedia_commons"];
relation["wikimedia_commons"];
);
out count;
Count number of addr:housenumber in an area
The following query counts the number of addr:housenumber=* nodes/ways/relations in an area. Duplicate addresses (i.e. multiple occurrences of the same address) are not filtered out. The query result is in csv format starting with the total number of addresses, followed by the number of nodes, ways and finally relations.
[out:csv(::count, ::"count:nodes", ::"count:ways", ::"count:relations")][timeout:25];
{{geocodeArea:Chemnitz}}->.searchArea;
(
node["addr:housenumber"](area.searchArea);
way["addr:housenumber"](area.searchArea);
relation["addr:housenumber"](area.searchArea);
);
out count;
Find relations with identical wikidata tags (since 0.7.54)
The following query determines all relations with admin_level=2 and wikidata=* tag, where the same wikidata
value is used across several relations.
As an example relations 1111111 is tagged as wikidata=Q183, which is also the case for relations 51477 and 62781.
[timeout:600];
rel[admin_level=2][wikidata]->.a;
foreach .a -> .b (
rel.a(if:t["wikidata"] == b.u(t["wikidata"]))->.d;
rel.d(if: id() == d.min(id()) && d.count(relations) > 1 );
convert rel ::id = _.u(id()),
wikidata = _.u(t["wikidata"]),
all_relation_ids = d.set( id() );
(._ ; .result; ) -> .result;
);
.result out;
Result:
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="Overpass API">
<note>The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.</note>
<meta osm_base="2017-03-29T19:04:03Z"/>
<rel id="11980">
<tag k="wikidata" v="Q142"/>
<tag k="all_relation_ids" v="11980;2202162"/>
</rel>
<rel id="36990">
<tag k="wikidata" v="Q235"/>
<tag k="all_relation_ids" v="1124039;36990"/>
</rel>
<rel id="51477">
<tag k="wikidata" v="Q183"/>
<tag k="all_relation_ids" v="1111111;51477;62781"/>
</rel>
<rel id="87565">
<tag k="wikidata" v="Q258"/>
<tag k="all_relation_ids" v="1252792;87565"/>
</rel>
</osm>
To speed up processing, an alternative approach is to export relevant fields' relation id and wikidata in CSV format and post-process the file via some shell script or similar:
[out:csv(wikidata, ::id;false;";")];
rel[admin_level][wikidata];
out;
Find deviating tagging
I'd like to find Objects where certain Keys have the same value. Irish streets normally have two names, one in Irish and one in English. In OSM they have three tags for that, name, name:en and name:ga. I'd like to find all objects having all three tags where name doesn't match with name:en or name:ga.
[timeout:300]
[bbox:51.08282186160976,-12.8759765625,55.986091533808384,-1.86767578125]
[out:csv(::id, ::type, name, "name:en", "name:ga")];
( node[name]["name:en"]["name:ga"](if:(t["name"] != t["name:en"]) && (t["name"] != t["name:ga"]));
way[name]["name:en"]["name:ga"] (if:(t["name"] != t["name:en"]) && (t["name"] != t["name:ga"]));
rel[name]["name:en"]["name:ga"] (if:(t["name"] != t["name:en"]) && (t["name"] != t["name:ga"]));
);
out;
The original question comes from this source.
Find wikidata relations having identical wikidata way members
The following query finds all wikidata relations in the current bounding box, where both the relation and the way member share the same wikidata id.
rel[wikidata]({{bbox}}); // get all relations with wikidata tag in current bounding box
foreach -> .rel( // process each relation one by one
// find out all way members where the wikidata tag matches the relation's wikidata tag
way[wikidata](r.rel)(if: t["wikidata"] == rel.u(t["wikidata"]))->.ways;
// if some way members could be found, print the current relation first...
rel.rel(if:ways.count(ways) > 0);
out center;
// ... followed by all ways with matching wikidata tag
.ways out tags;
);
Multipolygons with inappropiate member roles
The following query can be used to identify all multipolygon relations in the current bounding box, where not all of their members have an inner or outer role.
rel({{bbox}})[type=multipolygon]->.relations;
foreach .relations -> .relation (
(
node(r.relation);
way(r.relation);
rel(r.relation);
)->.elem_all;
(
node(r.relation:"inner");
way(r.relation:"inner");
rel(r.relation:"inner");
)->.elem_inner;
(
node(r.relation:"outer");
way(r.relation:"outer");
rel(r.relation:"outer");
)->.elem_outer;
rel.relation( if:elem_all.count(nodes) > elem_inner.count(nodes) +
elem_outer.count(nodes) ||
elem_all.count(ways) > elem_inner.count(ways) +
elem_outer.count(ways) ||
elem_all.count(relations) > elem_inner.count(relations) +
elem_outer.count(relations));
out center;
);
Relations with relation members only
Relation with relation members only cannot be queried easily using a bounding box. Given the example of administrative boundaries, the following query returns such relations on a global scale.
rel[boundary=administrative](if:count_members() > 0 && count_members() == count_members(relations));
out meta bb;
Geometry and Topology
Count number of ways and length
This tries to count the paths that are not used in cities with designated foot or bicycle traffic, i.e. paths in the woods.
[out:csv(number,length)][maxsize:1000000000];
{{geocodeArea:Sweden}}->.searchArea;
way["highway"="path"][!foot][!bicycle][!segregated](area.searchArea);
make stat number=count(ways),length=sum(length());
out;
Find multipolygons with more than one outer way member (since 0.7.54)
Please note that relations with more than one outer member are valid.
rel[type=multipolygon]({{bbox}});
foreach -> .a (
way(r.a:outer);
( rel.a(if:count(ways) > 1);
.result; )->.result;
);
.result out center;
Find multipolygons with a single outer way member (since 0.7.55)
The following query returns all multipolygon relations with a single way member.
[bbox:{{bbox}}];
rel[type=multipolygon](if:count_by_role("outer") == 1 && count_members(ways) == 1);
out geom;
Search for street intersections
The following query returns every intersection node between distinct streets (not including when a street changes its name or stops being a dual carriageway mid-block). [1]
[out:json][timeout:25][bbox:{{bbox}}];
// The public street network
way["highway"~"^(trunk|primary|secondary|tertiary|unclassified|residential)$"]->.streets;
// Get nodes that connect between three or more street segments
node(way_link.streets:3-)->.connections;
// Get intersections between distinct streets
foreach .connections->.connection(
// Get adjacent streets
way(bn.connection);
// If the names don't all match, add the node to the set of intersections
if (u(t["name"]) == "< multiple values found >") {
(.connection; .intersections;)->.intersections;
}
);
.intersections out geom;
This query excludes minor intersections that connect a public street with a service road, such as an alley or driveway. You can include these intersections by adding |service to the regular expression above. However, you may want to also filter such intersections by the presence of traffic control devices such as highway=traffic_signals or highway=stop.
Search for specific street intersections
The query above is overkill if you know something about the cross streets. The following query returns the street intersection of 6th Avenue and West 23rd Street (e.g. in New York) and is less likely to time out with a large bounding box:
[bbox:{{bbox}}];
way[highway][name="6th Avenue"]->.w1;
way[highway][name="West 23rd Street"]->.w2;
node(w.w1)(w.w2);
out;
Assumption: both streets need to be connected via a common node.
Note: If a way is contained in both .w1 and .w2 (e.g. because you match names using two regular expressions), all nodes of this way will be in the result set.
This query can find intersections of major and minor roads without a complex foreach
loop because the two sets of ways are disjoint:
[bbox:{{bbox}}];
way[highway~"^(motorway|trunk|primary|secondary|tertiary|(motorway|trunk|primary|secondary)_link)$"]->.major;
way[highway~"^(unclassified|residential|living_street|service|pedestrian|track)$"]->.minor;
node(w.major)(w.minor);
out;
This can be also adapted to find road-railway crossings and so on, and other cases where searching for nodes shared by both way with specific tag and way with other specific tag.
Complex Situations of Tagging and Geometry
Highway around schools with inappropriate maxspeed (since 0.7.54)
// see: http://blattspinat.com/index.php/posts/tempo-50-vor-schulen
[out:json][timeout:800];
// Get all schools in current bounding box
( way[amenity=school]({{bbox}});
node[amenity=school]({{bbox}});
rel[amenity=school]({{bbox}});
)->.schools;
// find highway=* around schools with radius 50m, ignoring
// footway and paths. In addition, highways are relevant if they
// either
// (1) have no maxspeed tag at all
// (2) maxspeed tag indicates a value larger than 30 km/h
way(around.schools:50)[highway][highway!~"^(footway|path)$"]
(if: (is_number(t["maxspeed"]) && t["maxspeed"] > 30) ||
!is_tag(maxspeed) )->.streets;
// get schools in 50m distance for all identified relevant streets
( node.schools(around.streets:50);
way.schools(around.streets:50);
rel.schools(around.streets:50);
)->.matchingSchools;
// return results, schools and streets
(.matchingSchools; .streets;);
out geom;
Isolated Buildings
Find buildings where the closest building is more than 1km away.
way[building]({{bbox}})->.a;
foreach .a (
way.a(around:1000);
way._(if:count(ways) == 1);
out center;
);
NB: Query is very slow, needs the following fix
Banks far away from police stations
Find banks where the closest police station is more than 3km away.
[out:json][bbox:{{bbox}}][timeout:800];
// determine set of police stations
(
node[amenity=police];
way[amenity=police];
rel[amenity=police];
)->.polices; // put them into the set "polices"
// determine set of banks
(
node[amenity=bank];
way[amenity=bank];
rel[amenity=bank];
)->.banks; // put them into the set "banks"
// determine set of banks near police stations
(
node.banks(around.polices:3000);
way.banks(around.polices:3000);
rel.banks(around.polices:3000);
)->.banksNearPolices; // put them into the set "banksNearPolices"
// determine banks far from police stations
(.banks; - .banksNearPolices;);
// return node, ways, relations as determined above
out geom meta;
City limit street signs
Tag:traffic_sign=city_limit allows putting a traffic_sign=city_limit node on the highway (rather than somewhere next to it). The following query identifies those standalone nodes, excluding those nodes which are in close proximity to an already correctly mapped city_limit node.
[bbox:{{bbox}}];
node[traffic_sign=city_limit] -> .allnodes;
way[highway](bn.allnodes);
node(w)[traffic_sign=city_limit];
node(around:15)[traffic_sign=city_limit] -> .waynodesandfriends;
(.allnodes; - .waynodesandfriends;);
out;
Pub tour in Dublin
Challenge: how far can you get with the next pub only 500m away?
node(3470507586); // starting here...
complete(100) { nwr[amenity=pub](around:500); };
out center;
golf=fairway outside leisure=golf_course
Challenge: find all fairways outside of any golf_course
[out:json][timeout:60][bbox:{{bbox}}];
// see https://forum.openstreetmap.org/viewtopic.php?id=70773 (in German)
// search all way/rel[leisure=golf_course] in bbox
wr[leisure=golf_course];
// convert to area
map_to_area ->.golfcourse;
// golf=fairway inside leisure=golf_course
way(area.golfcourse)[golf=fairway] -> .fairGolf;
// .fairGolf out;.fairGolf >;out geom;
// all golf=fairway
way[golf=fairway] -> .fairAll;
//.fairAll out;.fairAll >;out geom;
// form difference and output
(way.fairAll; -way.fairGolf;);
out; > ; out geom;
Find stairs at the end of a dead end road
Since release 0.7.61, a newly added filter way_link
can be used locate dead end nodes, see way_link
[out:json];
area[name="London"]->.a;
way(area.a)["highway"~"^(residential|living_street)$"];
node(way_link:1);
way(bn)["highway"="steps"];
out geom;
Calculate the length of a highway route
Overpass is more reliable than a routing engine for calculating the total length of a designated highway route. A routing engine may depart from the route in order to optimize for travel time or distance or avoid traffic congestion, but the length of a route per se, without any detours, is one of the basic attributes of the route.
U.S. Route 101 is one of the longest highway routes in California. Some but not all of this route runs along dual carriageway motorways. For technical reasons, the route is modeled as four relations: one route relation per direction, plus a bidirectional route relation for the rural northern segment. To obtain the "centerline" distance traveled by the route, we concatenate the southern leg's northbound route with the northern leg.
relation(id:108619,2306823);
make stats length=sum(length()) /* meters */;
out;
Estimate the distance to a motorway junction
In traffic management, it can be useful to know the distance along a highway route from its point of beginning up to some landmark, such as a motorway junction.
Clark County Route 215 forms part of a ring road around Las Vegas. Because each side of the ring is signposted with different cardinal directions, the route is modeled as a three-level hierarchy of route relations.
This query returns an estimated length from the beginning of the route going clockwise (westbound, then northbound, then eastbound) until exit 41. Normally, in the United States, an exit "41" would be about 41 miles (66 km) from the route's point of beginning. However, the exit numbers along this beltway are numbered from the beginning of an adjoining route, Interstate 215.
// Get the clockwise-bound relations of Clark County Route 215, one per leg.
relation(id:11605967,11605971,11620220)->.route;
// Recurse down to the constituent roadways.
way(r.route)->.roadways;
// Find exit 41 along the route’s roadways.
node(w.roadways)["highway"="motorway_junction"]["ref"="41"]->.exit;
// Get the first way along each route relation:
way(r.route)(
// Only if its ID is in the set of:
if: lrs_in(id(), route.set(
// For each member of the route relation:
per_member(
// The way if it is at position 1.
pos()==1 ? ref() : 0
)
))
)->.startingWay;
// Recurse up to the roadways before and after the exit.
way.roadways(bn.exit)->.exitAdjacentWays;
// Gather all the ways together.
(
// Start at the first roadway.
way.startingWay;
// Greedily expand the set as long as:
complete {
// The adjacent way is a roadway along the route and is not one of the ways adjacent to the exit.
(way.roadways(around:0);-way.exitAdjacentWays;);
};
// Also include the way before the exit.
// ASSUMPTION: The exit is at the end of the way.
way.exitAdjacentWays(around:0);
);
// Output the partial route.
out geom;
// Output the distance from the route’s starting point to the exit.
make stats length=sum(length());
out;
This query assumes that the highway=motorway way has been split at the highway=motorway_junction. If not, the returned distance would be an overestimate.
Users and old data
Find newbies mapping railway signals
Task: Find all unknown newbies in an area who have started mapping railway signals railway=signal after a point in time. If needed, help them with JOSM templates, etc.
node
[railway=signal](newer:"2017-01-01T07:00:00Z") // adjust date as needed
({{bbox}})->.newnodes;
// List all well-known power users here, which should be excluded from the result list:
(.newnodes; - node.newnodes(user:Nakaner, "Nakaner-repair", bigbug21, mapper999);)->.newnodes;
// Output
.newnodes out meta;
This query has a few shortcomings:
- Only last-changed timestamp of a node is taken into account. A non-railway mapper moving a railway=signal node would trigger false positives.
- The list of power users needs to be manually adjusted until the number of false positives is small enough.
Find all objects edited by a specific mapper
Task: Find all objects edited by a specific mapper.
Especially useful in tracking down damage caused by a vandal.
All features ever edited by a specific user:
nwr(user_touched:"Dzielny Fokowąs");
out skel qt;
All features last edited by a specific user:
nwr(user:"Dzielny Fokowąs");
out skel qt;
Note: For more reliable results, you should use the uid:
or uid_touched:
filter instead (see Overpass_API/Overpass_QL#By_user_(user,_uid). This way, you can still query for edits by a specific user, even if they delete their user, and re-create a new user with the same display name. uid:
also supports a list of user ids.
The easiest way to get uid is to follow "Changeset XML" at bottom of changeset made by a specific user.
OSM data at a certain date
The "date" parameter can be used to retrieve OSM objects as they were on a particular date, for example:
[date:"2014-05-06T00:00:00Z"];
( node({{bbox}}); <; >; );
out meta;
This can be used to see famous attempt of 2.5D modeling that was reverted fairly quickly as it was tagging for the renderer.
Another use-case is to find the OSM object id of a deleted object.
Old objects
Finds old buildings, not modified since specified date.
nwr[building]({{bbox}})->.all;
(.all; - nwr.all(newer:"2020-04-01T00:00:00Z");
);
out body;
>;
out skel qt;
Recently modified objects
Finds only buildings modified since specified date.
(
nwr[building]({{bbox}})(newer:"2020-04-01T00:00:00Z");
);
out body;
>;
out skel qt;
Making a Table of Object Versions
The purpose of retro plus timeline is to iterate over each version of an object and do something.
As you can get all object versions of an object from elsewhere as well, the purpose here is rather to pinpoint a property. This often works best if you make a table of the versions and the respective property.
We start with just version, timestamp, and changeset id.
[out:csv(version,timestamp,changeset)];
timeline(relation,2632934);
for (t["created"])
{
retro(_.val)
{
rel(2632934);
make stat version=u(version()),timestamp=u(timestamp()),changeset=u(changeset());
out;
}
}
We are free to use whatever makes sense within the inner brackets. In this case this is make with the three attributes version, timestamp, and changeset. They match the column titles as declared in out:csv at the top. The element name stat does not matter here because the CSV format does not print element names.
Now, each execution of out in the for loop produces one line.
In a second step, we add an extra attribute. The above mentioned relation got scrutiny because it errorneously had ten of thousands of members for a short time. We can add a column to count members. In this example we count with a flat count_members without restrictions on roles or member types:
[out:csv(version,timestamp,changeset,count)];
timeline(relation,2632934);
for (t["created"])
{
retro(_.val)
{
rel(2632934);
make stat version=u(version()),timestamp=u(timestamp()),changeset=u(changeset()),count=u(count_members());
out;
}
}
Relation 2632934 in this example has an exceptionally large number of relation members starting at version 99.
98 2018-12-10T15:05:22Z 343 99 2018-12-13T16:54:37Z 21002 100 2018-12-13T19:52:55Z 20846 101 2018-12-18T19:52:21Z 20910 102 2018-12-26T00:50:19Z 21161 103 2018-12-26T15:07:55Z 21097 104 2019-01-03T17:17:59Z 21223 105 2019-01-12T19:08:11Z 21223 106 2019-02-11T09:29:28Z 42698 107 2019-02-11T11:22:29Z 350
Exporting Data
Exporting Data has its own special requirements. As opposed to simply viewing data, it may make sense to have quite large amounts of data. There are some special precautions you should take in that case to be efficient with the resources of the public server. The second subject specific to data export is formatting the output in something different than the usual OSM XML.
List of streets
The following query allows you to compile a complete list of all street names in a town. The example is made for Troisdorf:
[out:csv("name";false)];
area[name="Troisdorf"];
way(area)[highway][name];
out;
This gives all the Ways inside this town, including ways that cross the town boundary. It cannot be avoided, but luckily it does not occur that often. The Nodes of the ways are not returned.
One only has to remove duplicates from this list, this can easily be done on the command line with
sort liste.csv | uniq
one can extract the list of street names.
By using a for
statement, it is also possible to sort the list and remove any duplicates:
[out:csv("name";false)];
area[name="Troisdorf"];
way(area)[highway][name];
for (t["name"])
(
make x name=_.val;
out;
);
In many cases, the boundary of a town has a different name than the city itself. This leads to empty results.E.g. Eckernförde requires the suffix "Landmasse" . The problem can be solved in general by using a regular expression
[out:csv("name";false)];
area[name~"Eckernförde"];
way(area)[highway][name];
out;
Adding geocoding details to POIs
Original question on help.openstreetmap.org:
I have a Overpass API query which returns lot of nodes. I would like to know whether there is a way to add reverse geocoding information to the resulting data set. Details of my case are not that important, but just for completeness:
area[name="Česko"];
node[information="guidepost"](area);
out;
In fact I do not need full reverse geocoding info for nodes, I only need relation of certain administrative level, so if this is possible to add into the result set, it would be great.
Solution:
area[name="Česko"];
node[information="guidepost"][ele~"^2..$"](area)->.posts;
// You can remove "ele".
// It has been added to experiment with a fewer number of results
foreach.posts(
out;
is_in;
area._[admin_level~"[46]"];
// The choice of "[46]" is arbitrary and could be anything else.
// In this case, admin_level=4 are counties and admin_level=6 are cities,
// which might be handy.
out ids;
);
// collect area details in 2nd step
.posts is_in;
area._[admin_level~"[46]"];
out;
To avoid repeating full area details for each information=guidepost, the query uses the following alternative approach: for each guidepost, we will only print the osm id for the area, but nothing else. Only towards the end of the query, the full details for all relevant areas are returned.
Here's a brief example on how the format looks like: a POI (node 691566183) is followed by two area ids (3600435511 and 3600442466), which are followed by another POI and its respective area ids. To find out the details for area id 3600435511, simply consult the full area details towards the end of the response.
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="Overpass API">
<node id="691566183" lat="49.7982193" lon="13.4686623">
<tag k="ele" v="295"/>
<tag k="information" v="guidepost"/>
<tag k="name" v="Dolanský most (pravý břeh)"/>
<tag k="tourism" v="information"/>
</node>
<area id="3600435511"/>
<area id="3600442466"/>
<node id="691566191" lat="49.8003120" lon="13.4679726">
<tag k="ele" v="295"/>
<tag k="information" v="guidepost"/>
<tag k="name" v="Dolanský most (levý břeh)"/>
<tag k="tourism" v="information"/>
</node>
<area id="3600435511"/>
<area id="3600442466"/>
[...]
<area id="3600435511">
<tag k="admin_level" v="4"/>
<tag k="boundary" v="administrative"/>
<tag k="name" v="Jihozápad"/>
<tag k="name:cs" v="Jihozápad"/>
<tag k="name:de" v="Südwesten"/>
<tag k="population" v="1209298"/>
<tag k="ref" v="CZ03"/>
<tag k="ref:NUTS" v="CZ03"/>
<tag k="source" v="cuzk:ruian"/>
<tag k="source:population" v="csu:uir-zsj"/>
<tag k="type" v="boundary"/>
<tag k="wikipedia" v="cs:NUTS Jihozápad"/>
</area>
<area id="3600442466">
<tag k="ISO3166-2" v="CZ-PL"/>
<tag k="admin_level" v="6"/>
<tag k="boundary" v="administrative"/>
<tag k="name" v="Plzeňský kraj"/>
<tag k="name:cs" v="Plzeňský kraj"/>
<tag k="name:de" v="Region Pilsen"/>
<tag k="name:ru" v="Пльзенский край"/>
<tag k="population" v="572687"/>
<tag k="ref" v="PL"/>
<tag k="ref:NUTS" v="CZ032"/>
<tag k="source" v="cuzk:ruian"/>
<tag k="source:population" v="csu:uir-zsj"/>
<tag k="type" v="boundary"/>
<tag k="wikipedia" v="cs:Plzeňský kraj"/>
</area>
Source, Thanks to Roland
Adding Georeference details to village nodes (since 0.7.54)
Since 0.7.54, it is also possible to generate is_in
details for place=village nodes in the current bbox on-the-fly:
node({{bbox}})[place=village];
foreach(
is_in->.a;
area.a[name][boundary=administrative][admin_level~"^[2-8]$"] -> .a;
convert node ::=::,
::id = id(),
is_in=a.set("{" + t["admin_level"] + ":" + t["name"] + "}");
out;
);
Performance hint: don't run this kind of query on 1000 or 10000+ objects on the overpass-api.de instance, as it consumes too much resources.
The resulting XML includes a new is_in
tag, which lists all boundary=administrative with admin_level from 2..8 and a name, sorted by increasing admin_level:
<node id="1724711941">
<tag k="name" v="Steinklingen"/>
<tag k="place" v="village"/>
<tag k="is_in" v="{2:Deutschland};{4:Baden-Württemberg};{5:Regierungsbezirk Karlsruhe};{6:Rhein-Neckar-Kreis};{8:Weinheim}"/>
</node>
Unfortunately, any nodes which have been enhanced via convert
don't include any lat/lon details at this time.
- Church building ways in an area
When applying this technique to ways, one has to keep in mind that the is_in
statement only works on nodes. For this reason, we first have to determine all nodes for a way.
[timeout:60];
area["name"="Mayenne"]->.boundaryarea;
way(area.boundaryarea)["building"="church"];
foreach(
node(w)->.n; // determine all nodes of a way
.n is_in->.a;
area.a[name][boundary=administrative][admin_level~"^[2-8]$"] -> .a;
out center;
convert way ::=::,
::id = id(),
is_in=a.set("{" + t["admin_level"] + ":" + t["name"] + "}");
out;
);
Wiki table generator (since 0.7.54)
The following query abuses csv and make features a bit to create a wikitable syntax table, which can be copy & pasted into a wiki page (see result below). This might be useful to avoid command line post processing of Overpass results.
// Poor man's wikitable generator
[out:csv(_row;false)];
make out _row = "{|class=wikitable "; out;
make out _row = "|- "; out;
make out _row = "! Regional Key "; out;
make out _row = "! Name "; out;
make out _row = "! Total "; out;
make out _row = "! Nodes "; out;
make out _row = "! Ways "; out;
make out _row = "! Relations "; out;
//All areas with regional key (German: "Regionalschlüssel") starting with 057
area["de:regionalschluessel"~"^05774"];
// Count the pharmacies in each area
foreach->.regio(
// Collect all Nodes, Ways and Relations wth amenity=pharmacy in the current area
( node(area.regio)[amenity=pharmacy];
way(area.regio)[amenity=pharmacy];
rel(area.regio)[amenity=pharmacy];) -> .r;
make out _row = "|-"; out;
make out _row = "| " + regio.set(t[ "de:regionalschluessel"]) +
" || " + regio.set(t["name"]) +
" || " + (r.count(nodes) + r.count(ways) + r.count(relations)) +
" || " + r.count(nodes) +
" || " + r.count(ways) +
" || " + r.count(relations);
out;
);
make out _row = "|}"; out;
Query result (pasted into a wiki page):
Raw data | Rendered as wiki page | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
{|class=wikitable
|-
! Regional Key
! Name
! Total
! Nodes
! Ways
! Relations
|-
| 05774 || Kreis Paderborn || 68 || 61 || 7 || 0
|-
| 057740016016 || Büren || 3 || 3 || 0 || 0
|-
| 057740036036 || Salzkotten || 5 || 5 || 0 || 0
|-
| 057740040040 || Bad Wünnenberg || 3 || 2 || 1 || 0
|-
| 057740028028 || Lichtenau || 2 || 2 || 0 || 0
|-
| 057740024024 || Hövelhof || 3 || 2 || 1 || 0
|-
| 057740020020 || Delbrück || 6 || 6 || 0 || 0
|-
| 057740008008 || Bad Lippspringe || 4 || 4 || 0 || 0
|-
| 057740032032 || Paderborn || 39 || 35 || 4 || 0
|-
| 057740012012 || Borchen || 1 || 1 || 0 || 0
|-
| 057740004004 || Altenbeken || 2 || 1 || 1 || 0
|} |
|
All data
(
node({{bbox}});
<;
);
out meta;
Overpass_API/Advanced_examples#Example:_Simplest_possible_map_call
Quality Assurance
The general purpose of the Quality Assurance Queries is to check whether some assumption about the data universally holds. Thus, the themes covered here are converting assumptions into query conditions and mastering the functionality to understand museum data.
See QA Tool Overpass Queries for a mapping of Overpass queries based on common Quality Assurance tools such as Osmose, KeepRight, JOSM Validator, OSM Lint, and Atlas.
Checking boundary relations for correct tagging
Looking for a boundary relation with boundary=administrative and admin_level=9 and de:amtlicher_gemeindeschluessel=XXXXXXXX, because this combination is invalid. Boundary relation for villages with their own "Gemeindeschlüssel" should have at least admin_level=8 or higher.
The first query also leaves out "boundary=administrative", because it is equally possible that one has set the wrong level or did not mention the level at all
rel[admin_level=9]["de:amtlicher_gemeindeschluessel"];
out;
or
rel[boundary=administrative][admin_level=9]["de:amtlicher_gemeindeschluessel"];
out;
Finding relation members with incorrectly spelled member roles
Find boundary=* relations with an incorrectly spelled member role admin_center
(should be admin_centre
)
rel[boundary](if:count_by_role("admin_center") > 0);out meta;
Find all bus stops which are not included in a relation
This query returns all bus stop nodes, which are not part of a relation.
// Search bus stops without relation
// Select city of Bonn as area
area[name="Bonn"];
// Collect all bus stops in Bonn and store the result in a variable .all
node(area)[highway=bus_stop]->.all;
// Select all relations, where one of the nodes in variable .all is a member
rel(bn.all);
// ...and for those relations find all related node members
node(r)->.b;
// Calculate the set difference (.b contains all nodes which are member of a relation).
( .all; - .b; );
// Return the result including meta data (needed for JOSM exports).
out meta;
// Variant for line 9 / 10:
// Return only relations with a specific tagging:
// rel[route=bus](bn.all);
(via talk-de, Thanks to Roland)
Bus relations, which are not part of a master relation
Q: I need to find out, which bus relations (route=bus) are not part of a master relation (route_master=bus) in a given area.
rel({{bbox}})[route=bus]->.all;
rel[route_master=bus](br.all);
rel[route=bus](r)->.b;
(.all; - .b; );
out;
Step-by-step explanation:
- Determine all relations in the current bbox with type route=bus. Save the result in variable .all
- Determine all parent relations of our relations in .all. Those relations need to be of type route_master=bus.
- Based on the list of route_master relations, we determine all child relations of type route=bus. Those relations are all part of a master relation.
- Create the set difference of the relations in variable .all and the result from the previous step 3. This way, all relations, which are not part of a master relation, are returned.
Communities without fire station
The following alternative gives all communities [admin_level=8] (Gemeinden) in Schleswig-Holstein without amenity=fire_station. The result is in CSV format and contains the OSM ID of the relation, the "amtlichen Gemeindeschlüssel" and the name of the relation.
// Output in CSV format
[out:csv(::id, "de:amtlicher_gemeindeschluessel", name)];
area[admin_level=4]["name"="Schleswig-Holstein"][boundary=administrative]->.boundaryarea;
// All bounding areas with level 8 in Schleswig-Holstein
rel(area.boundaryarea)[admin_level=8];
// map relations to their respective area
map_to_area -> .all_level_8_areas;
// Search All amenity=fire_station in Schleswig-Holstein
( node(area.boundaryarea)["amenity"="fire_station"];
way(area.boundaryarea)["amenity"="fire_station"];>;);
// in which boundary areas with level 8 do they appear?
is_in;
area._[admin_level=8] -> .level_8_areas_with_firestation;
// All level 8 bounding areas without those
// areas with fire station
(.all_level_8_areas; - .level_8_areas_with_firestation;);
// Calculate and output relation
rel(pivot);
out geom;
Buildings crossed by roads that are not tagged as tunnel or covered
Query for buildings crossed by roads (Ways) that do not have a tunnel or covered tag. Since query uses the "around" operator, which searches in the environment of an item, it is possible that false positives are returned, e.g. when a street (Way) is directly connected to the building
Please note that this is a very slow query, that can better be run with a specified bounding box.
You may want to add also highway=path, highway=footway, highway=cycleway, highway=steps to the query (not roads, but it is the same type of the problem).
highway=road may turn out to be something else, but typically is also a road and you may want to include it.
way[!covered][!tunnel]
["highway"~"motorway|motorway_link|trunk|trunk_link|primary|primary_link|secondary|secondary_link|tertiary|tertiary_link|service|residential|unclassified|living_street|pedestrian|track"]["access"!~"no|private"][!area]
({{bbox}});
(way(around:0)["building"~"."];
node(w););
out meta;
Buildings passages tagged as tunnels
Query for paths and footways tagged with tunnel=yes, then filtered by those with correct geometry (sharing node with building) and others. To print those with incorrect geometry uncomment last line and comment previous one.
Please note that this is a very slow query, that can better be run with a specified bounding box.
(way["highway"~"path|footway"][tunnel=yes]({{bbox}});) -> .all_tunnels;
(way(around.all_tunnels:0)[building];) ->.crossed_buildings;
(way.all_tunnels(around.crossed_buildings:0);) -> .tunnels;
(.crossed_buildings >;) -> .building_nodes;
( .tunnels > ; - way.all_tunnels;) -> .tunnel_nodes;
(node.tunnel_nodes.building_nodes;) -> .good_geometry;
( .good_geometry <;) -> .searchhere;
(way.searchhere.tunnels;); // comment this line to see only those with incorrect geometry
//(.tunnels; - way.searchhere.tunnels;); // uncomment this line to see only those with incorrect geometry
Find duplicate nodes (since 0.7.54)
See source.
We iterate over all nodes in the current bounding box one by one, and find out the list of all nodes with the same lat/lon value. Assuming there's more than one node at the same lat/lon, those two nodes will have different node ids. That's also the condition we use to filter out those duplicate nodes. The output will only return the duplicate node for each location having the lowest node id.
[bbox:{{bbox}}];
node;
foreach->.a(
node(around.a:0)->.b;
node.b(if:id() == b.min(id()) && b.min(id()) != b.max(id()));
out ;
);
Unfortunately, this is very slow.
Find duplicate nearby nodes with same tag
Find peaks that are close to each other and share the same name:
node["natural"="peak"]({{bbox}})->.a;
foreach.a->.elem(
node.a(around.elem:50)
(if:is_tag("name")&&t["name"]==elem.u(t["name"]));
(._; - .elem;);
out meta;
);
Overpass Turbo link (with "date" for demo purposes) source (de)
Search for untagged ways
way({{bbox}})(if:count_tags() == 0);
out geom;
Before version 0.7.54 the following query can be run in overpass turbo and results exported to JOSM for further processing.
(way({{bbox}}); - way({{bbox}})[~"."~"."];)->.w1;
rel(bw.w1);way(r)->.w2;
(.w1; - .w2;);
(._; >;);
out meta;
Note that it will find ways where tagging is valid (e.g., members of multipolygons).
Alternative: OSM Inspector has a Tagging layer highlighting ways without tags that applies additional filters, JOSM validator or Osmose.
Search for ways with just area=yes
[timeout:200];
way({{bbox}})[area=yes](if:count_tags() == 1);
out geom;
Such ways are never correct and always need fixing (sometimes adding proper tags, sometimes deletion, sometimes removal of area=yes).
Orphaned nodes (nodes without tags, not part of any way or relation)
The following query checks for nodes in the current bounding box without any tags, that are not part of any way or relation.
[bbox:{{bbox}}];
rel; > -> .r;
way; > -> .w;
(( node(if:count_tags()==0); - node.r; ); - node.w; );
out meta;
Find broken associatedStreet-relations
This query finds associatedStreet relations corrupted in some typical ways, for example ones generated by JOSM-plugin "terracer" that used to create by default a broken associatedStreet-relations.
Note that opinion differs what should be done with such relation - in Germany mappers agree that such relations should be deleted as useless, in France mappers prefer to repair such relations.
/* Enter your city/region/etc. here */
{{nominatimArea:Berlin}}
(._; )->.area;
/* Find all associatedStreet relations in your area */
rel[type=associatedStreet]
[!name]
[!"addr:street"]
(area.area)->.allASRelations;
/* Find associatedStreet relations with member ways having role "street" */
way(r.allASRelations:"street");
rel(bw:"street")[type=associatedStreet]->.relationsWithRoleStreet;
/* Find all aS relations with empty role-way members */
way(r.allASRelations:"");
rel(bw:"")[type=associatedStreet]->.relationsWithEmptyRole;
/* Find all relations with node members */
node(r.allASRelations);
rel(bn)[type=associatedStreet]->.relationsWithNode;
/* Check if there are member ways with role 'house' not having a building tag */
way(r.allASRelations:"house")[!building];
rel(bn)[type=associatedStreet]->.relationsWoHouseBuilding;
/* Take all aS relations and remove all relations we want to exclude */
( ( ( (.allASRelations; - .relationsWithRoleStreet;);
- .relationsWithEmptyRole; );
- .relationsWithNode; );
- .relationsWoHouseBuilding);
/* add ways and nodes for output*/
(._; );
out meta;
Find house numbers without street
The following Overpass API query retrieves all Nodes, Ways and Relations that have a addr:housenumber-tag and no addr:street nor addr:place tag. For this collection, we remove all Nodes/Ways/Relations that take part in an associatedStreet-Relation with a role "house".
Notes:
- The query assumes that the associatedStreet-relation is conformant with the wiki and that it contains at least one member with the role "street", such that the street can be deduced. This is not verified by the query!
- associatedStreet-Relations without street-roles can be found with the query "Find broken associatedStreet-relations" . This problem has to be resolved first, as it would prevent the correct retrieval of house numbers.
- The name-tag on the relation is ignored. Warning: in the solution of User:Gehrke for #osmwa3334 requires a name tag on the associatedStreet-Relation, but ignores the members with the role "street"
The search area can be adapted in the first line, more details in Forum Thread.
area[type=boundary]["de:amtlicher_gemeindeschluessel"="08115003"]->.boundaryarea;
rel(area.boundaryarea)[type=associatedStreet]->.associatedStreet;
way(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesWay;
way(r.associatedStreet:"house")->.asHouseWay;
((.allHousesWay; - .asHouseWay;); >; );out meta;
node(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesNode;
node(r.associatedStreet:"house")->.asHouseNode;
((.allHousesNode; - .asHouseNode;););out meta;
rel(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesRel;
rel(r.associatedStreet:"house")->.asHouseRel;
((.allHousesRel; - .asHouseRel;); >>; );out meta;
Alternatively, one could do a search via Nominatim:
{{nominatimArea:"Regionalverband Saarbrücken"}}
(._; )->.boundaryarea;
/* Shows the boundary area as extra check - one is free to activate this step */
/* rel(pivot.boundaryarea);(way(r);>;);out; */
rel(area.boundaryarea)[type=associatedStreet]->.associatedStreet;
way(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesWay;
way(r.associatedStreet:"house")->.asHouseWay;
((.allHousesWay; - .asHouseWay); >; );out meta;
node(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesNode;
node(r.associatedStreet:"house")->.asHouseNode;
((.allHousesNode; - .asHouseNode););out meta;
rel(area.boundaryarea)["addr:housenumber"][!"addr:street"][!"addr:place"]->.allHousesRel;
rel(r.associatedStreet:"house")->.asHouseRel;
((.allHousesRel; - .asHouseRel); >>; );out meta;
Nehme entweder "addr:street", "addr:place" oder "name" von associatedStreet. Trifft nichts zu, ist die Straße "null". Ich zähle nun alle Fälle (nur nodes und ways), wo es zwar eine Hausnummer gibt, aber die Straße "null"
Non-matching addr:postcode=XXXXX tags inside a boundary relation with postal code ≠ XXXXX
... what is the query for all Nodes and Ways with a different addr:postcode than the surrounding boundary relation?
- Query for a specific postal code, e.g. "42103":
area[postal_code="42103"]->.a;
(node(area.a)["addr:postcode"]["addr:postcode"!="42103"];
way(area.a)["addr:postcode"]["addr:postcode"!="42103"];);
out;
Non-matching addr:postcode=XXXXX tags inside multiple boundary relation with postal code ≠ XXXXX (since 0.7.54)
Since release 0.7.54, it's also possible to check several postal codes in one query:
area[postal_code](if:number(t[postal_code])>="42103" &&
number(t[postal_code])<="42119")->.pc;
foreach.pc -> .a(
.a out;
(
node(area.a)["addr:postcode"]["addr:postcode"](if: t["addr:postcode"] != a.u(t["postal_code"]));
way(area.a) ["addr:postcode"]["addr:postcode"](if: t["addr:postcode"] != a.u(t["postal_code"]));
);
out geom;
);
The overpass turbo friendly version first determines all postal_code boundary relations in the current bounding box and checks for deviations in nodes and ways, comparing the postal_code with the addr:postcode tag.
[bbox:{{bbox}}];
rel[boundary=postal_code][postal_code];
map_to_area;
foreach -> .a(
(
node(area.a)["addr:postcode"](if:t["addr:postcode"] != a.u(t["postal_code"]));
way (area.a)["addr:postcode"](if:t["addr:postcode"] != a.u(t["postal_code"]));
// rel(area.a)["addr:postcode"](if:t["addr:postcode"] != a.u(t["postal_code"]));
);
out geom;
);
Previously, this required a bash-script for postal codes 42103 - 42119
i=42103;
while [[ $i -le 42119 ]];
do {
wget -O false_$i.osm "http://overpass-api.de/api/interpreter?data=area[postal_code=\"$i\"]->.a;(node(area.a)[\"addr:postcode\"][\"addr:postcode\"!=\"$i\"];way(area.a)[\"addr:postcode\"][\"addr:postcode\"!=\"$i\"];>;);out;";
i=$(($i + 1));
};
done
When one replaces "out;" with "out meta;", than it becomes possible to read the data in the file "false_XXXXX.osm" into JOSM for editing.
The query becomes noticeably faster with a proper Bounding Box:
i=42103;
while [[ $i -le 42119 ]];
do {
wget -O false_$i.osm "http://overpass-api.de/api/interpreter?data=area[postal_code=\"$i\"]->.a;(node(51,7,51.5,7.5)(area.a)[\"addr:postcode\"][\"addr:postcode\"!=\"$i\"];way(51,7,51.5,7.5)(area.a)[\"addr:postcode\"][\"addr:postcode\"!=\"$i\"];>;);out meta;";
i=$(($i + 1));
};
done
Ways that are part of a "Bundesstraßenrelation", but do not have a ref-tag
- Bundesstraßenrelation or National road network (in English)
Normally, one would expect that the properties of a relation are also applied to the member ways, so that it is not needed to set those tags explicitly on the ways. However, it turns out that the common practice is to set this ref also on each way. This query shows all ways that do not have the ref-tag.
rel({{bbox}})[type=route][route=road][ref~"^B."];
way(r)[!ref];
(._;>;);
out meta;
It is also possible to have a ref tag, but that the way does not belong to a relation.
area[name="Saarland"][type=boundary]->.a;
(
way(area.a)[highway][ref~"^B.*"]; -
(rel(area.a)[ref~"^B.*"][route=road][type=route];>;);
);
(._;>;);out;
For a selected area (in the example Saarland), the query retrieves all Ways whose ref-tag starts with a "B". From this collection, we remove all Ways that are part of a relation with type=route, route=road and that have a ref that starts with "B". The remaining federal streets, which are not part of a relation, are returned together with their nodes, in order to show them on a map.
POIs with websites but with incorrect opening hours
The following query returns institutions and shops, for which a website is tagged, but the opening hours are incorrect (i.e. the syntax is wrong). One should first try the first query, because it more likely to return results for which the opening hours can be found on the website. When the result of the first query is empty, one could try the second one.
Objects with outdated or missing opening hours and collection times (for post boxes)
The below query is tailored to highlight objects with outdated or missing opening hours and collection times (for post boxes). You can use it to prepare a map with places to visit at a mapping party.
/* load all node/ways/relations with opening_hours set and being unequal 24/7 and store them*/
nwr[opening_hours][opening_hours!="24/7"]({{bbox}})->.oh_all;
/* remove younger entries from the previous result and store them again */
(.oh_all; - nwr.oh_all(newer:"2019-04-01T00:00:00Z");)->.oh_old;
/* do something similar as above with collection_times */
nwr[collection_times]({{bbox}})->.ct_all;
(.ct_all; - nwr.ct_all(newer:"2019-04-01T00:00:00Z");)->.ct_old;
/* unify previous results with further queries and store them */
(
.oh_old;
.ct_old;
/* look for amenities without opening_hours and which are not private (exempt amenities that usually don't carry opening_hours)*/
nwr[amenity][!"opening_hours"]["access"!="private"]["amenity"!~"waste_disposal|waste_basket|bench|vending_machine|post_box|fountain|kindergarten|parking_entrance|bus_station|shelter|drinking_water|watering_place|school|college|university|bicycle_parking|bbq|fire_station|hunting_stand|doctors|dentist|clock"]({{bbox}});
/* shops without opening_hours */
nwr[shop][!"opening_hours"]({{bbox}});
/* post_boxes without collection times */
nwr["amenity"="post_box"][!"collection_times"]({{bbox}});
);
out center meta qt;
Some known weaknesses:
- The decision if an opening hour (or collection time) is outdated is done based on the date when any tag of this object was changed: I.e. an object may appear as not outdated even though the tag opening_hours=* was created a long time ago.
- The date to consider objects outdated is both hardcoded and used in multiple places in the query.
- Some amenities usually don't have opening hours (since they are open 24/7, e.g. benches). The query contains a (black-)list of such values and will never report them. There certainly are further (rare) amenity=* tags that are not included (yet).
Highlight Objects with a check_date older than X
The example above used a hard-coded date and searched for the time an object was edited for the last time. If a 'check_date' tag is present, this can be searched for as well.
This query uses the 'convert' feature of Overpass to add custom tags on objects that are older than a given date. These tags can then be used in a MapCSS to highlight certain ages of edits. Please note that the output of this query can be used only in Overpass Turbo - the added tags make the format non-standard and the data can't be loaded e.g. to JOSM.
Note: Source code shown is shortened to one age check and to use nodes only, see this link for the full version
[out:json][timeout:25];
nwr({{bbox}})[check_date]->.temp;
.temp out body;
.temp >;
out skel;
node.temp;
convert node
::id=id(),
older365=date(t["check_date"])<date("{{date:365days}}")
;
out;
{{style:
node[older365=1]{
color: yellow;
}
}}
//[...]
Buildings that are taller than they are wide
The query below tries to find any building in Cincinnati, Ohio, and surrounding Hamilton County that is taller than it is wide. Overpass QL lacks an evaluator that evaluates to the area of a closed way; it only has an evaluator for the length of a way, or the perimeter in the case of a closed way. So this query finds the average width of one of the building's walls based on the perimeter and compares it to the building height. For simplicity, it only considers rectangular buildings. A more sophisticated query could inspect buildings with more vertices but divide the perimeter by only the vertices at approximately right angles.
This query finds much more than skyscrapers and high-rises. An import of government building data in the county included floor counts of many buildings, but the source data had incorrect floor counts for many small buildings. For example, it is rare for a backyard toolshed or garage to have more than one level, but many are tagged as having more than one level.
Elsewhere, extremely tall buildings due to typos have garnered amusement from end users and the media. [2][3][4] This query would easily catch such mistakes, though a simpler query for abnormally large numbers would likely perform better.
[out:json][timeout:25];
// Most buildings in Hamilton County, Ohio, were imported from CAGIS, which assigned questionable floor counts to small buildings in many cases
{{geocodeArea:Hamilton County, Ohio, United States}}->.hamco;
// Find rectangular buildings that are taller then they are wide
(
// Compare the height to the average length of a side
way["building"]["height"](area.hamco)(if:
count_members() < 6 && is_closed() &&
number(t["height"]) > length() / 4);
// Assume a floor is 3 m tall
way["building"]["building:levels"](area.hamco)(if:
count_members() < 6 && is_closed() &&
number(t["building:levels"]) * 3 > length() / 4);
);
// Print results
out body;
>;
out skel qt;
Case-insensitive search for suspicious names
It is using regexp for case insensitive matching (triggered by ",i").
Note that thanks to ^ (matching text start) and $ (matching text end) it will not find everything with "pond" within, just names that are such as "pond", "Pond", "pOnD"
Note that this query finds all cases worldwide.
[out:xml][timeout:250];
(
nwr[natural=water]['name'~'^pond$',i];
);
out meta;
>;
out meta qt;
Building Blocks
These are examples that are not directly related to applications but rather support the language specification with examples.
Search objects with a certain tag A but without a tag B
Example: find all Nodes, Ways and Areas which are tagged with addr:city, but do not have addr:postcode. This makes use of the exists filter and not exists filter.
[out:json][timeout:25];
(
node["addr:street"][!"addr:postcode"]({{bbox}});
way["addr:street"][!"addr:postcode"]({{bbox}});
);
out skel;
Find nodes/ways with a given role in a relation
Find all nodes with role "label" or "admin_centre" in current bounding box:
rel({{bbox}})->.relations;
( node(r.relations:"label");
node(r.relations:"admin_centre");
);
out;
Find all "inner" ways, i.e. relation member ways with role 'inner':
rel({{bbox}});(way(r:"inner");>;);out;
Find all nodes and ways with role "via":
rel({{bbox}})->.relations;
(
node(r.relations:"via");
way(r.relations:"via");>;
);out;
Sum of a few defined OSM areas
This query is easily modified to get a sum of any OSM defined areas (geocodeArea) and extract any data for this sum of areas. See comments for details. The example just shows the chosen areas.
[out:json][timeout:900];
// get a few areas into .myArea
// you can add or remove an area by simply adding or removing
// one line here.
(
{{geocodeArea:"Warszawa"}};
{{geocodeArea:"Grodzisk Mazowiecki County"}}; // "name:en" may be used as well as "name"
{{geocodeArea:"powiat pruszkowski"}};
{{geocodeArea:"powiat żyrardowski"}};
{{geocodeArea:"powiat warszawski zachodni"}};
{{geocodeArea:"powiat sochaczewski"}};
{{geocodeArea:"powiat nowodworski"}};
{{geocodeArea:"powiat legionowski"}};
{{geocodeArea:"powiat wołomiński"}};
{{geocodeArea:"powiat miński"}};
{{geocodeArea:"powiat otwocki"}};
{{geocodeArea:"powiat piaseczyński"}};
)->.myArea;
// display .myArea This can be replaced by any query
// on the objects in .myArea
rel(pivot.myArea);
// print results
out geom;
{{style:
node{opacity:0;fill-opacity:0}
}}
« n » adjacent ways
The following example illustrates how to determine five adjacent ways for OSM way 111435507. The around statement with radius 0, which is used in this query not only filters directly connected ways, but also returns intersecting ways (without a common node). Possibly those intersecting ways need to be filtered out in a post-processing step.
(
way(111435507);
complete(4) {
way(around:0)[highway~"."][highway!~"path|track|cycleway|footway"];
};
);
out geom;
To limit the overall data volume only ways with a highway tag (excluding path, track, cycleway and footpath) are considered.
Search for nodes which belong to two different ways
How to find nodes, which belong to two different ways with certain tags?
Examples:
Returns nodes which are part of a building:
way["building"="yes"]({{bbox}});
node(w);
Returns nodes of tertiary highways:
way["highway"="tertiary"]({{bbox}});
node(w);
How can I combine those two queries so that both conditions are met?
way["building"="yes"]({{bbox}});
node(w)->.b;
way["highway"="tertiary"]({{bbox}});
node(w)->.t;
node.b.t; // calculates set intersection of both variables .b and .t
out;
If ways cannot be split into two distinct groups (e.g. building ways vs. highway=tertiary ways), it is necessary to iterate over each single way and determine the intersecting nodes with all other ways in a bbox:
way({{bbox}})->.allways;
foreach .allways -> .currentway(
(.allways; - .currentway;)->.allotherways;
node(w.currentway)->.e;
node(w.allotherways)->.f;
node.e.f;
(._ ; .result;) -> .result;
);
.result out meta;
Search for nodes which belong to an exact number of ways (since 0.7.54)
To find out all nodes, which belong to an exact number of ways (rather than just more than one way as in the previous example), the following approach can be used:
Find out all nodes, which belong to exactly 3 distinct ways:
way({{bbox}})->.a;
foreach .a -> .b(
(.a; - .b;)->.c;
node(w.c)->.e;
node(w.b)->.f;
node.e.f->.f1;
foreach .f1 -> .g(
way(bn.g);
way._(if:count(ways) == 3);
node.g(w);
(._ ; .result;) -> .result;
);
);
.result out meta;
Caveat: Unfortunately, this query is extremely slow.
Retrieve full object history (since 0.7.55)
The following query example returns all versions of a given way.
timeline(way,561040198);
foreach(
retro(u(t["created"]))
(
way(561040198);
out meta geom;
);
);
Note: Object history does not include object versions, which are not part of the first ODbL licensed planet or any later minutely diff (=cutoff date September 2012).
Limit results to areas only
The following query example returns specific tag (man_made=pier in this example), but limited to closed ways and multipolygon relations. Nodes, unclosed ways and other relations are not returned.
Example run in Kraków - as it is one of places with piers mapped both as lines and area.
[out:json][timeout:25];
{{geocodeArea:Kraków}}->.searchArea;
(
way["man_made"="pier"](if: is_closed()==1)(area.searchArea);
relation["man_made"="pier"]["type"="multipolygon"](area.searchArea);
);
out body;
>;
out skel qt;
Examples for Styles in overpass turbo (MapCSS)
Hiking routes
Simple way
The following query for Overpass Turbo shows all hiking-relations in the current bounding box (route=hiking and network=?wn), such as in the Lonvia-style. The color for the ways is taken from the relation.
[bbox:{{bbox}}];
(
relation[route=hiking][network~"^.wn$"];
way(r);
>;
);
out;
{{style:
way
{ color:green; fill-color:green; }
relation[network=lwn] way
{ color:blue; fill-color:cyan; }
relation[network=iwn] way
{ color:red; fill-color:red; }
relation[network=nwn] way
{ color:green; fill-color:green; }
relation[network=rwn] way
{ color:yellow; fill-color:yellow; }
}}
It is possible that this is not 100% correct, please test and adapt when needed. (received via osm-dev, MapCSS was corrected, Query simplified)
Advanced example
In this example apart from route geometry we output center point - and print route symbol (osmc:symbol=*, hotlinked to [5] tool). Working example can be some region of Poland: Overpass.
[bbox:{{bbox}}];
relation[route=hiking];
out geom center;
>;
out;
{{style:
relation {
color:gray;
}
relation[colour] {
color: eval("tag('colour')");
text: ref;
}
way, node[!route] {
fill-color: transparent;
color:transparent;
}
node[route] {
fill-color: eval("tag('colour')");
color: eval("tag('colour')");
text: eval("concat(tag('name'), ' - (', tag('from'), ' - ', tag('to'), ') - ', tag('ref'))");
icon-image: eval("concat('url', '("', 'https://osm.janmichel.eu/osmc/generate.pl?osmc=', tag('osmc:symbol'), '")')");
}
}}
Textboxes with eval
The following MapCSS style displays all villages in the current bounding box with a combined German / Lower Sorbian textbox.
[bbox:{{bbox}}];
node[place=village];
out;
{{style:
node{
text: eval('tag("name:de") . " - " . tag("name:dsb")');
}
}}
Mailboxes
This thema can be analysed with the queries below
Mailboxes, coloured depending on availability of the collection time tag
node[amenity=post_box]
//[!"collection_times"]
({{bbox}});
out;
{{style:
node
{ color:red; fill-color: red;}
node[collection_times]
{ color:green; fill-color:green; }
}}
Alternative that also checks the syntax of the collection time
Overpass Turbo Link - valid for 2022
node[amenity=post_box] ({{bbox}});
out;
{{style:
node{
color: #ffffff;
fill-color: #ff0000;
fill-opacity: 0.8;
}
node[operator]{
color: #0000ff;
fill-color: #ff0000;
fill-opacity: 0.8;
}
node[collection_times:lastcheck],
node[last_check],
node[check_date],
node[last_checked],
node[lastcheck]{
color: #ffff00;
fill-color: #ffff00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck][operator],
node[check_date][operator],
node[last_check][operator],
node[last_checked][operator],
node[lastcheck][operator] {
color: #0000ff;
fill-color: #ffff00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2022-*./],
node[check_date=~/2022-*./],
node[last_check=~/2022-*./],
node[last_checked=~/2022-*./],
node[lastcheck=~/2022-*./]{
color: #ffff00;
fill-color: #00ff00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2022-*./][operator],
node[check_date=~/2022-*./][operator],
node[last_check=~/2022-*./][operator],
node[last_checked=~/2022-*./][operator],
node[lastcheck=~/2022-*./][operator]{
color: #0000ff;
fill-color: #00ff00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2021-*./],
node[check_date=~/2021-*./],
node[last_check=~/2021-*./],
node[last_checked=~/2021-*./],
node[lastcheck=~/2021-*./]{
color: #ffff00;
fill-color: #55aa00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2021-*. /][operator],
node[check_date=~/2021-*./][operator],
node[last_check=~/2021-*./][operator],
node[last_checked=~/2021-*./][operator],
node[lastcheck=~/2021-*./][operator]{
color: #0000ff;
fill-color: #55aa00;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2020-*./],
node[check_date=~/2020-*./],
node[last_check=~/2020-*./],
node[last_checked=~/2020-*./],
node[lastcheck=~/2020-*./]{
color: #ffff00;
fill-color: #aa5500;
fill-opacity: 0.8;
}
node[collection_times:lastcheck=~/2020-*./][operator],
node[check_date=~/2020-*./][operator],
node[last_check=~/2020-*./][operator],
node[last_checked=~/2020-*./][operator],
node[lastcheck=~/2020-*./][operator]{
color: #0000ff;
fill-color: #aa5500;
fill-opacity: 0.8;
}
}}
More complex recursion
Example: Simplest possible map call
This call includes all nodes in the bounding box, all ways that have such a node as member, and all relations that have such a node or such a way as members. Please observe that not all returned ways or relations are displayed in Overpass Turbo because it requires all nodes of a way or members of a relation to be present.
(
node(50.746,7.154,50.748,7.157);
<;
);
out meta;
|
The operator "<" does here all the required backwards resolution of membership links of ways and relations. You could also break this up into step-by-step backwards recursion:
- First, get all relations linking to the just found nodes: rel(bn);
- Then, get all ways linking to the just found nodes: way(bn);
- Finally, get all relations linking to the just found ways: rel(bw);
To complete the list of backwards membership resolution, rel(br); would find relations that have the just found relations as members.
(
node(50.746,7.154,50.748,7.157);
rel(bn)->.x;
way(bn);
rel(bw);
);
out meta;
|
Display result: (Overpass Turbo doesn't show), JSON, XML.
Completed ways, but not relations
This call includes all nodes in the bounding box, all ways in the bounding box, all member nodes of these ways whether these are inside the bounding box or not, and all relations that have such a node or such a way as members. For the moment, the call of way with a bounding box makes the query sometimes slow. I will work on it, but please be patient for the moment. Please observe that not all returned relations are displayed in Overpass Turbo because Overpass Turbo requires all nodes of a way to be present.
(
node(50.746,7.154,50.748,7.157);
rel(bn)->.x;
way(50.746,7.154,50.748,7.157);
node(w)->.x;
rel(bw);
);
out meta;
|
Display result in an OpenLayers map OpenLayers map.
Completed ways and relations
This call includes all nodes in the bounding box, all ways referring to any of these nodes, all member nodes of these ways whether these are inside the bounding box or not, all relations that have such a node or such a way as a member, and their node and way member, and even the nodes of these ways. Note that due to the involved relations, objects from quite far outside the bounding box are included.
(
node(50.746,7.154,50.748,7.157);
<;
>;
);
out meta;
|
Display result: OpenLayers map.
Here "<" does backwards membership resolution and ">" does forward membership resolution. In particular, ">" collects for all just found relations their member nodes, their member ways, and the member nodes of these member ways. This again can also be broken down into its building blocks:
- node(w) collects the nodes that are members of the just found ways
- node(r) does the same with the just found relations
- way(r) collects the ways that are members of the just found relations
The statement rel(r) collects the relations that are members of the just found relations, but it is not invoked here. Now, the same query as before in a more step-by-step fashion:
(
node(50.746,7.154,50.748,7.157);
rel(bn)->.x;
way(bn);
rel(bw);
);
(
._;
way(r);
);
(
._;
node(r)->.x;
node(w);
);
out meta;
|
Display result: OpenLayers map.
Also relations on relations
This call includes additionally all relations on relations that after five steps of descent arrive at members in the bounding box. Their members are deliberately not included because that might end up with a significant part of all map data of Germany; i.e., several hundred megabyte of data. Note also that five levels of descent usually is more than enough to get all backlinks. If you doubt you have all backlinks, just add another rel(br); statement and see whether more data comes back.
(
node(50.746,7.154,50.748,7.157);
rel(bn)->.x;
way(bn);
rel(bw);
);
(
._;
way(r);
);
(
._;
node(r)->.x;
node(w);
);
(
._;
rel(br);
rel(br);
rel(br);
rel(br);
);
out meta;
|
Display result: OpenLayers map.
Server Status Queries
If you're self hosting an OverPass API server it can be useful to query it to check it's health.
Data Last Updated
Every query returns an `osm_base` timestamp which tells you the last time the data was updated (see this question on the OSM Help Forum for more details). In XML it's `<meta osm_base="2020-07-01T11:54:03Z"/>` and in JSON it's under `osm3s.timestamp_osm_base`. You can make any query to retrieve this date, use `out count;` to minimise the amount of data returned by not including any nodes.
(
node(50.746,7.154,50.748,7.157);
<;
);
out count;
|
See also
Internal links
- (en) User:Binnette/OverpassQueries: some queries for France, Europe and MapRoulette challenges
- (en) User:M!dgard/Overpass queries: some queries for Belgium and wrong tagging check
- (en) PDF How to get maps with special content? with OVERPASS TURBO
- (en) SPARQL vs. Overpass QL examples
- (de) User:EinKonstanzer/overpass: some queries for bench, schools, Düsseldorf, ... (en) Translated version
- (it) PDF Overpass examples
- (fr) PDF Les requêtes pour Overpass Turbo
- (en) Example Overpass queries specific to OpenHistoricalMap