Mapping public transport in Belgium

Posted by Polyglot on 10 January 2015 in English (English). Last updated on 14 January 2015.

Naamsepoort with line 616 highlighted by MapCSS This article is inspired by but adapted to how public transport is mapped in Belgium at the moment.

I also recorded an editing session in JOSM:

Add bus route in Antwerp Belgium

Apparently public transport companies do all the major changes to their time tables towards year’s end. At least this seems the case for both Germany and Belgium. So we have some changes to process in Belgium as well.

In Germany they have weekly assignments. I guess that, over here, cleaning up the mapping of public transport, will become a task, that takes somewhat longer than a week to accomplish.

We also still have quite a large amount of lines that are mapped in the now deprecated way, using just one route in a futile attempt to describe all its variations. If all you wanted to do, is render on a map where the buses pass, this may be sufficient, but in case you want to describe actual itineraries, that is severely lacking. Those older routes take the longest to convert, but at least there is an approximate indication of the itinerary when they are present.

We have, however, many routes which are already on ‘version 2’. The problem with those is that they are prone to change to the timetables/itineraries and they get broken by edits with inferior editors, which don’t process relations properly.

This got me thinking about a way to:

  1. detect that these routes are in need of attention?

  2. keep them up-to-date in a practical way?

I’ve been working on scripts to automate the boring parts of this arduous task. The source code can be found here:

and here:

Incidentally those scripts also help when creating routes for bus and tram lines which aren’t in OSM yet.

They depend heavily on the availability of PT data from De Lijn and TEC though. So their performance is optimal when such data is available, which isn’t the case for the Brussels’ operator MIVB/STIB, unfortunately. Those can be added in more traditional ways, but instead of starting from the stops to add an itinerary afterwards, it may make more sense to work the other way around.

On to the translation/adaptation of the German week assignment

Mapping public transport in OSM is a subject which is often poorly understood. The consequence is a multitude broken route relations, which don’t get updated. For users of the data this is rather worthless. At the time of writing, it seems like 3, and possibly a few other, mapping schemes are in use (I found out recently that we deviate from what the wiki proscribes in Belgium as well, this article was started to describe at which points in detail).


Before starting to edit, some research might be needed. Does the route_master relation still describe an existing bus/tram line? Do the route relations still describe actual variations accurately?

Sometimes lines get new identifiers (line numbers). This occurred in Beringen recently:

Beringen krijgt nieuw busnet vanaf 14 juli

I worked on updating those over the past week (Jan 2015). The changes happened in July 2014 though, I’m working on a way to detect such changes in a more timely way and to present to the community at large through a web interface.


To map public transport, you’ll need an editor which can work correctly with relations, iD is not up to that task, as it’s not possible to influence the order of the members with iD. And the order of relation members is crucially important in public transport route relations. (It is important in our cycle/walking node relations as well, if you want to do automatic validation on them, but that’s another story).

JOSM is the only suitable editor to accomplish this task and not only because of the relation editor, but also because it’s the only editor which supports the MapCSS to properly visualise the data and a scripting environment to use the scripts I created to help with the task.


New routes and all the ones we bring up-to-date, should be mapped according to the ‘new’ Scheme. For Belgium it’s described here: Stops/Stations

In the new scheme a separation is made between where the passengers wait and where the (front of the) bus/tram/train halts on the way. The latter is mapped as a node which is part of the way. Although all stops of De Lijn and TEC are now in OSM, these stop_positions were not always added. Please add them if you are already editing/splitting the way for other purposes.

for bus stops add bus=yes
for tram stops add tram=yes
for train add train=yes

These nodes don’t get extra information like name, ref, route_ref or zone. (at least in Belgium they don’t)

When this serves as a terminal/initial stop for some of the served line variations, split the way at this point.


In case a platform is present, this can be entered as a way or an area.


To get them rendered add:

highway=platform for bus stops


railway=platform for tram stops

In case of a closed way for an area add


At the moment I haven’t found a reason to use multipolygon relations for these.

In Belgium, these platforms don’t get any extra tags, except for tactile_paving=yes/no if a way was used to map it and those foot massagers are present.

In case of an area was used for mapping the platform, it’s probably better to add a highway=footway with tactile_paving=yes. The imagery of AGIV is good enough to determine where their extremities are. They look like white T’s

Platform nodes

To keep things simple and manageable all bus and tram stops in Belgium are mapped on nodes. I never considered moving the details like name, ref, route_ref and zone to platform ways, as that would result in them not being shown anymore by the MapCSS style I created for that purpose.

These nodes can designate the position of the pole, a corner on the shelter where the ‘flag’ is mounted, or if this is unknown, the position of the B of the word B U S, if visible on the aerial imagery.

In Belgium all bus and tram stops of De Lijn and TEC are now mapped, most of them by yours truly. (Click on the links if you want to download files converted to OSM format for verification). In case the position could not be determined from the aerial imagery these positions are approximate until somebody surveys and moves them.

TEC also provides shapefiles, which I converted to GPX. This is mostly interesting to know which streets the buses are likely to follow and to exclude the streets where they most certainly don’t go.

In Brussels we can’t seem to get permission to access to the data of MIVB/STIB, but the exact positions of the poles are in the dataset of UrbIS, which we are allowed to use. So we should be able to map the PT network in Brussels the (mostly) traditional way. UrbIS contains the tram rails as well. It’s quite an undertaking to integrate them in OSM, but once they’re in, it becomes trivial to add the tram lines. If you provide me with GPX files of your bus rides on lines in Brussels, I might even add/update them for you.

Some stops of De Lijn and TEC extend into the Brussels Region, The Netherlands, Germany, Luxembourg and France. All these were added to OSM as well. In fact, I did those first, to find out which was the best way to do so. At first I used dedicated nodes per operator, but that’s not the proper way, after all (I was never entirely happy with doing it that way, but it had a few advantages as well in case some fields didn’t happen to agree). Now each operator got their own ‘namespace’ and those nodes should all be merged by now.,

for bus stops add bus=yes
for tram stops add tram=yes
for train add train=yes

For backward compatibility and to get them rendered:

add highway=bus_stop or railway=tram_stop

All bus/tram stop nodes have a unique ref to identify them. To make it possible to use a single node for multiple operators, these are mapped as:

ref:De_Lijn ref:TECB ref:TECC ref:TECH ref:TECN ref:TECL ref:TECX

Indeed, each entity of TEC assigned their own identifiers and for buses which wander into another provinces the stops have more than one.

For Veolia, Connexxion, MIVB/STIB, etc we can safely use:


But mostly we don’t have identifiers for these.

Sometimes it’s necessary to use separate namespaces for name as well:



When 2 entities of TEC don’t happen to agree on the name to use, I just picked 1, mostly arbitrarily.

At some point we decided to include the name of the municipality as part of the name:

Blanden Kerk

Haasrode Kerk

Making an exception for the larger cities like Antwerp, Brussels and Ghent. In retrospect I would prefer to start prepending Gent as well. For Charleroi and Liège, I simply included them as part of the name. For stops outside of Belgium this is not customary. For bilingual areas, I also dropped the village/city names, as they become rather long otherwise.

The same applies to route_ref. This tag includes all the lines which serve this bus stop.

For QC purposes one could also include not_served_by for those stops skipped by express buses, or when it’s not possible to serve them as the bus makes a left turn across 2 lanes soon after where the halt is located.

It’s possible to include

* shelter=yes/no
* covered=yes/no
* bench=yes/no
* bin=yes/no

Or it’s possible to map these as separate elements:

* amenity=shelter
    * shelter_type=public_transport
* amenity=bench
* amenity=waste_basket
* building=roof


for bus stations you can add a node or an area tagged as 

* amenity=bus_station

stop_area relations

All of the above can be collected in a stop_area relation. One stop_area relation for each highway=platform node. So in simple cases one for each side of the road.

stop_area_group Relations

All stop_area relations can be collected in stop_area_group relations. JOSM will complain about these. Let it. The validator may catch up some day, or not. They were in the initial ‘new’ PT scheme, and then they were dropped for some inexplicable reason.

Once the stops are mapped according to ‘PTv2’ - we can start adding route relations for all the variations. The good news is that in Belgium almost all stops have been added by now. The exception are those stops in Brussels which don’t get served by De Lijn or TEC. Add OSM notes to the map with all their details and send me a link to the note for those, if you don’t feel comfortable adding them directly.

Route Relations

In PTv2 there is one route relation per variant. So the simplest lines will have 2 variants, one for each direction (The exceptions confirming the rule are the Ringbus 600 and 601 in Leuven and some school buses like 586). The good news is that we can compile route relations for each of these variants automatically for lines run by De Lijn and TEC.

To keep the number of variants (and the work to keep them up-to-date) limited we only have a variant for the longest sequence of stops for telescopic routes. I believe it makes sense to save contributors’ precious time and to limit the number of route relations, ways need to be members of. For rendering there is no difference (except that it means less processing) and for routing, it’s relatively easy to cut out the ways sequence which is needed for a shorter sequence of stops. (I know it can be done, as I’ve scripted exactly that to assist contributors when creating new route relations)

Each route relation has all the stops at the end. These are the platform NODES mentioned higher up. These are the only nodes the conversion scripts know about, so these are the nodes which can be added automatically to the template routes it creates. Their order matters, of course. At some point I made the arbitrary decision to have the ways first, followed by the stops, as that’s the way JOSM would sort them (if you were foolish enough to let it do that on all the members of the relation at once. JOSM can sort the ways fine, but it’s smarter to give it a subset of just ways. It doesn’t cope very well if ways are used more than once (loops, spoons)).

Apparently the wiki contradicts this and proscribes stops first, then ways. Ultimately I don’t think it matters much, unless it would cause ‘edit wars’. I prefer to have the ways first, as that is what contributors work on. The stops are added by a script, at least in our case.

If the bus uses a way more than once, it will be in the route more than once. The same goes for the stops, which get served more than once by the same variant. This is less common than ways being used more than once.

Master_route Relations

All variant routes belonging to the same line are collected in a master route relation.

Challenge: keep it all up-to-date

I will come up with a script which can make an overview of which routes are in need of attention. Hopefully I can get an account on a dev server to do so.

Location: 1730, Flemish Brabant, 1730, Belgium

Comment from jgpacker on 19 January 2015 at 17:56

Hi, as far as I understood the reference codes are for public transport systems in a specific country, correct? Lately there is a trend to add a prefix to country-specific tags, such as ref:FR:NAF for a reference code specific to France. I believe the country code for Belgium is BE, so the reference codes would be ref:BE:TECC, ref:BE:De_lijn and so on. (this is only a suggestion)

One more comment: would it make sense to use ref:BE:TEC:C=* instead of ref:BE:TECC=* ?

Comment from Polyglot on 19 January 2015 at 18:13

On the one hand, it might. On the other hand it would Megan changing 70000 nodes once again. The scripts I’m developing depend on these tags, so they need to be changed wholesale. Doing it gradually would take ages and it wouln’t be practical. I wasn’t aware of this convention to add BE. I’ll keep it in mind if I feel compelled to make a change to a large amount of them for other reasons. It’s unlikely such need will arise during the next few months or even years, though.

Login to leave a comment