Overpass API > Blog >

Some State of the Public Transport

Published: 2017-04-24, updated 2019-10-31

Table of content

Maybe for Buses
Not Calling for Passengers
Somewhere in Europe

Remarks

I have just finished to reorganize the examples page in the wiki. The reorganization may or may not help in navigating the page. My motivation is to understand what the goals are that the Overpass API users are after. I will revise the individual items later on.

As it turns out, one large subject is to understand and count tagging combinations. This is a good opportunity to answer some recurring questions about how public transport tagging is actually in the field.

Maybe for Buses

The most loudly announced issue in the Public Transport v2 scheme is the transition from highway=bus_stop to public_transport=platform and public_transport=stop_position. The former is for bus stops that are modeled by their sign beside the road. The latter is for bus stops that are modeled by an estimation of the stop position of the vehicle. We will first take care of the platforms.

Along with public_transport=platform these stops should have been tagged bus=yes to distinguish these platforms from platforms for other means of transport. So we have already three tags to care for: highway=bus_stop, public_transport=platform, and bus=yes.

A clean solution would be to check any of the eight possible combinations. However, this example should tell you to notice when we do self-deception. I do it right now and take the unchecked assumptions that:

This means we have to master the following building blocks:

  1. Collect all nodes that have any of the two tags public_transport=platform or highway=bus_stop!
  2. Figure out the fraction of named objects amongst them!
  3. Tell the fraction of nodes amongst all public_transport=platform nodes that have a bus=yes or bus=no tag!

From these requirements I suggest:

area[name="Antwerpen"];
( node(area)[highway=bus_stop];
  node(area)[public_transport=platform]; );
node._[name]->.with_name;
node._[public_transport=platform]->.pt;
node.pt[bus=no]->.not_bus;
node.pt[bus=yes]->.explicit_bus;
make count
  all=count(nodes),
  with_name=with_name.count(nodes),
  pt=pt.count(nodes),
  not_bus=not_bus.count(nodes),
  explicit_bus=explicit_bus.count(nodes);
out;

Let us reassign the lines to the requirements:

  1. Lines 2 to 3 contain the usual union statement with two usual query statements inside.
  2. Line 4 copies from all found elements only those that have a name tag. This is pretty fast by using the item filter to reuse the previous result.
  3. In a similar manner, we copy in lines 5 to 7 in a cascade elements to get the number nodes tagged as public_transport=platform and bus=yes or bus=no.

For the friends of advanced presentation, a variant that really writes fractions in the usual percent notation:

area[name="Antwerpen"];
( node(area)[highway=bus_stop];
  node(area)[public_transport=platform]; );
node._[name]->.with_name;
node._[public_transport=platform]->.pt;
node.pt[bus=no]->.not_bus;
node.pt[bus=yes]->.explicit_bus;
make count
  all=count(nodes),
  with_name=with_name.count(nodes)/count(nodes)*100 + " %",
  pt=pt.count(nodes)/count(nodes)*100 + " %",
  not_bus=not_bus.count(nodes)/pt.count(nodes)*100 + " %",
  explicit_bus=explicit_bus.count(nodes)/pt.count(nodes)*100 + " %";
out;

It looks like at least bus=yes has been carefully applied everywhere - and that the Antwerp tram and light rail is modeled in a somehow different way.

Not Calling for Passengers

Now to the nodes with public_transport=stop_position - it is time for another unchecked assumption: there are no stops where passengers can neither board nor alight (really? And houses always have entrances?) Hence we search for stop positions that have no nearby platforms.

This can be archived in the following steps: First, being close to is always reciprocal. Thus, it is always worth consideration whether we search for one side or the other first. I opt for searching for platforms here. Second, platforms can be nodes, ways, or relations. Thus we need the usual construction of an union statement and multiple query statements.

We cut out of all the stop positions those stop positions that are close to a platform, i.e. at most 10 meters away from a platform:

area[name="Milano"]->.a;
( node(area.a)[public_transport=platform];
  way(area.a)[public_transport=platform];
  rel(area.a)[public_transport=platform]; )->.platforms;
( node(area.a)[public_transport=stop_position];
  - node._(around.platforms:10)->.matched; )->.orphans;
make count
  all=count(nodes),
  orphans=orphans.count(nodes);
out;

This number may justify the hypothesis that there are still a lot of unmatched stop positions.

Somewhere in Europe

It is time to cross-check whether our test cities are representative. For the sake of comfort the two queries can be merged into a single query. To get a concise overview it is best to take a sample fo cities across Europe and to make a table out of the results:

[out:csv("name", "all", "orphans", "with_name", "pt", "not_bus", "explicit_bus")];
( area[name="Hamburg"]["admin_level"=4];
area[name~"^(München|Köln|Milano|Napoli|Birmingham|Manchester|Barcelona|Antwerpen)$"]["admin_level"=6];
  area[name~"^(Lille|Lyon|Marseille)$"]["admin_level"=7];
  area[name="Rotterdam"]["admin_level"=8];
)->.areas;

foreach.areas->.a(
  ( node(area.a)[public_transport=platform];
    way(area.a)[public_transport=platform];
    rel(area.a)[public_transport=platform]; )->.platforms;
  ( node(area.a)[public_transport=stop_position];
    - node._(around.platforms:10); )->.orphans;
  ( node.platforms;
    node(area.a)[highway=bus_stop]; )->.stops;
  node.stops[name]->.with_name;
  node.stops[public_transport=platform]->.pt;
  node.pt[bus=no]->.not_bus;
  node.pt[bus=yes]->.explicit_bus;
  make count
    name=a.set(t["name"]),
    all=stops.count(nodes),
    orphans=orphans.count(nodes),
    with_name=with_name.count(nodes),
    pt=pt.count(nodes),
    not_bus=not_bus.count(nodes),
    explicit_bus=explicit_bus.count(nodes);
  out;
);

I got on 24 Apr 2017 the results:

nameallorphanswith_nameptnot_busexplicit_bus
Manchester100000
Birmingham399118393314209
Barcelona339776626855740360
Marseille233071229771217
Napoli1710344123570065
Lyon37483393712357003522
Milano452953540078410687
München22157122061280122
Lille2873149284227110450
Antwerpen75541117519752107300
Rotterdam737481733137094
Köln165930716485280135
Hamburg37664103717115011110

Hence, we can state that