The total mess of tagging crossings and a way to move forward
Posted by Pieter Vander Vennet on 19 December 2025 in English. Last updated on 21 December 2025.In the ancient days, OSM was much simpler. You had the streets and roads.
A crossing was considered a barrier along the way for road users (i.e. cars), so one would place a node with highway=crossing where the crosswalk is located. The routeplanner would apply a time penalty, the rendering engine shows a little icon, and done.
With an extra attribute (namely crossing_ref) a more precise type and the type of markings was indicated.
Then, someone figured out they could also shove in information about the traffic signals.
This kind of works for simple crossings, but breaks down for bigger intersections. crossing=unmarked, crossing=uncontrolled and crossing=controlled were added. And crossing=zebra, cause zebra crossings are so common. And crossing=marked, which was almost, but not quite, entirely unlike ~~tea~~ crossing=uncontrolled.
In the mean time, people started mapping cycleways and sidewalks/footways separately,
giving rise to a second way of tagging crossings: along the way; as highway=footway with footway=crossing.
Some ported over the traffic signal metadata over on the way, as they were used to doing on a node. Others starting mapping the traffic lights separately, often as barrier at the precise location where the pedestrian/cyclist/car driver has to wait.
This gives the potential of having the traffic light metadata on three separate locations:
1) Once on the point where the road and the footway meet 2) Once on the footway, marked with crossing 3) Once on the traffic light node on the footway
In other words, the current tagging is a clusterfuck.
Why do I care?
I currently got a (commissioned) project where the people want to input advanced properties of bicycle infrastructure, so I got confronted with this mess. And, in contrast to a certain someone else who only needs to render items, I need to write data, so need to actually improve the tagging. He actually did quite a good writeup on this topic, it was a good overview for me as well and I'm building a bit on his diary entry! Go read it. And thank for all your other contributions!
What is wrong with using a simple node
The simplicity of having a single node telling there is a crossroads with lights is good, but doesn’t scale well.
Properties of the lights (e.g. the amount of time to wait, wether or not it is button-operated, …) might be different depending on where a person comes from and goes to, so it doesn’t quite capture all the complexities I’d like to capture..
The value
My main problem with the crossing-tag on nodes is that it tries to capture to much information.
The value of the crossing already tries to combine several concepts:
- what markings there are (if any):
markedvsunmarked(and possiblezebraor some other values) - if there are traffic lights:
traffic_signalsvsuncontrolled
Note that uncontrolled does imply - according to the wiki - that there are markings; but this might not have been clear to all mappers.
In some countries, having traffic lights might imply having certain markings - or rather, might imply having a legal obligation for road management to also paint certain markings. However, human error and changing laws already make this an incorrect assumption.
I would propose to completely drop crossing=zebra|marked|uncontrolled|... and use crossing:markings instead, eventually with a traffic_light node
crossing_ref
Crossing ref (luckily) has also seen it’s peak; usage has halved.
It has it’s fair share of problem, mostly that it uses opaque animal names that are only well understood by someone that has lived their entire lives in the UK. It scales badly and has been supplanted by the clearer, more generally applicable crossing:markings-key.
Imho, the time is ripe to deprecate crossing_ref and to start retagging it.
About traffic signals
Imagine a single crossroad (with four incoming ways) for car traffic, with 4 zebra crossings - one on each incoming road. This means we’ll have twelve traffic lights: 4 traffic lights to control the incoming car traffic and a pair of pedestrian traffic lights for every zebra crossing.
There might be huge difference between those traffic lights. A common example is when a major car road crosses a minor road. Some differences might be:
- The pedestrians crossing the major road will have longer wait times (on average)
- The pedestrians crossing the major road might have a button they have to press to request green…
- … whereas the pedestrians crossing the minor road don’t have such a button, as they’ll get green regularly
- The sound and/or vibration signals might be different for every traffic light; i.e. if a signal broke down or didn’t get installed (correctly).
In other words, trying to map ‘traffic_light=yes’ on a highway=crossing_node is only good for a first mapping but fails to allow enough detail.
If the crossing is mapped as a highway, I propose the traffic lights are always mapped as a point in the way and not as attributes on the way. We can (as compromise) add traffic_signals=separate on the crossing to indicate to data consumers to look for the connected traffic lights; but this should be a temporary solution.
Button-operated?
Another problem with traffic lights is button_operated, which covers a great range of possibilities. Those buttons, mostly found on pedestrian crossings (and on bicycle crossings, at least in belgium), are generally tagged with button_operated=yes
button_operated=yes might mean a pedestrian/cyclist:
- needs to press the button, otherwise he’ll never be able to cross
- pedestrian/cyclists might get a shorter wait time if pressed
- can press the button, but nothing happens
- might press the button, and depending on the hour or the day, any of the above might happen. E.g. at night, they must to press the button to get green, during rush hour the button does nothing and during daytime, they get a shorter wait time.
And there are also a few cases where there is a button to trigger the sound or vibration signals for deaf/blind/… people. The contributor might have mistaken the button for a button influencing the greenlight-cycle.
I propose the following tags:
button_operated=yes: there is a button which might influence the green cycle (i.e. is unrelated to the buttons for sound/vibration)button_operated=required(orbutton_operated=only): one must press the button to crossbutton_operated=reduce_wait_time: one may press the button and one might get a reduced wait time, possibly only at certain times of daybutton_operated=fake: there is a button, but it does nothingbutton_operated=broken: the button is clearly broken, i.e. it is unresponsivebutton_operated=no: there is no button to press
I’m not yet sure about buttons where the effect depends on the time of day. Perhaps button_operated=required @ 22:00-06:00; reduced-wait-time @ 06:00-22:00?
Traffic islands
A similar confusing and duplicate data story unfolded with traffic islands. These too can be mapped on two separate location in the database:
- by using highway=footway + footway=traffic_island which has 5M entries
- by using traffic_island=yes on a crossing object
Here too, I’d propose to drop traffic_island=yes alltogether and draw separate lines.
Some usage stats:
footway=traffic_island 97K entries
cycleway=traffic_island 5.5K entries
crossing=traffic_island negligable
Mixing in cycleways
Cycleway crossings all have the same problems. Furthermore, it seems like highway=crossing is assumed to be a pedestrian crossing.
To make matters even worse, we sometimes have a pedestrian crossing (typically zebra) and a cycleway crossing right next to each other, oftentimes mapped as a single line…
These too should be mapped as two lines, including the traffic lights on them. In some (rare) cases, the pedestrians and cyclists get green at different times. For example, this crossing for pedestrians and cyclists has the following cycle for traffic lights:
- cyclists and pedestrians are allowed to cross
- cars from the north-west (Bevrijdingslaan) are allowed to turn left towards Guldenvlieslaan
- cars from the north-west (Bevrijdingslaan) are allowed to continue, traffic from the north is allowed to merge in
- only cyclists are allowed to cross north+south
- cars are allowed to go from/to north-west to/from south-east
So, all these complicated situations are clearly indicating that the current tagging model can barely cope with the current complexities.
Let’s improve the tagging scheme
With this opinion, I’d like to open the discussion on tagging crossings and improve it
Discussion
Comment from Ruben Van de Velde on 20 December 2025 at 12:20
“fourteen” should be 12, right?
I wonder how verifiable the button_operated values are. V-plans aren’t usually (as far as I’m aware, in Flanders) public by default, though perhaps they’re FOI-eligible? And that’s assuming they’re implemented correctly, and none of the sensors are broken (I’m aware of both happening here).
I wasn’t aware of
traffic_island=yesbeing used at all given we havecrossing:island; I removed one case in Brussels.Splitting pedestrian and cyclist crossing seems reasonable, though I don’t always want to spend the time to do it - especially in the case where the sidewalk and cycleway parallel to the road are not mapped separately.
Another fun example that I see quite often, e.g. at Antwerpsesteenweg x Oude Bareelstraat:
Here the pedestrian crossings over the cycleway don’t have traffic lights, but those over the car lanes do. I guess the fully detailed mapping would add
footway=sidewalks parallel to the cycleway, and tag the crossings as (NW to SE) 1. bit offootway=sidewalk2.footway=crossing;crossing=uncontrolled3.footway=traffic_island4.footway=crossing;crossing=traffic_signal(just one, because no traffic island in this case, but often two with anotherfootway=traffic_islandbetween) 5.footway=traffic_island6.footway=crossing;crossing=uncontrolled7. bit offootway=sidewalkwhich is… a bit much :)
Comment from mactavishfr on 21 December 2025 at 10:04
I agree with your suggestions. Crosswalks have become a real headache on OSM.
Comment from Pieter Vander Vennet on 21 December 2025 at 12:42
Re @Ruben:
Indeed, 12 lights. Seems like I miscounted; I’ve edited it.
Re: button_operated: indeed, it sometimes cannot be determined. We should keep
=yesin this case. When it can be determined, we have the different values.Re: lot of work: I don’t have the time and energy to do all of this as well. However, some data users (i.e. blind people, municipality workers, people lobbying for cycle infra) need this data. We might be to lazy to do this, but we shouldn’t be stopping them either!
Comment from Ruben Van de Velde on 21 December 2025 at 12:51
Oh, absolutely not arguing that it’s bad to do the very detailed mapping. But I might need to choose between mapping one intersection to that detail, or ten in a more simplified way, and then I’m not sure that doing the one is overall more valuable. So I’d leave some room for multiple tiers of mapping like the Pedestrian Working Group uses.
Comment from flohoff on 21 December 2025 at 14:22
Please be careful here.
In routing a highway=traffic_lights is something completely different than highway=crossing + crossing=traffic_lights.
The first has a penalty, the second does not. The issue is that a traffic light which has regular intervals has a statistical delay/penalty. A pure pedestrian crossing which is only button operated e.g. on demand does not have that penalty.
So moving every pedestrian crossing into highway=traffic_lights is a significant information loss which makes routing worse.
Flo
Comment from Lejun on 24 December 2025 at 05:27
My only “issue” with your idea is about the traffic signals. While that may be fine as a first approach to insert the traffic light into the road way, that’d be opposed to the “one object, one element” rule where one have multiple lanes drawn. Furthermore, I’d rather like to have the exact position of the traffic light rather than where one is supposed to stop. Should we keep highway=traffic_lights for actual poles location (how about lights hanging on top of lanes?), and create a highway=stop=traffic_lights?. That’s only my opinion.
Comment from Pieter Vander Vennet on 26 December 2025 at 14:49
@Lejun Ooh, that is an interesting one!
What I’ve seen in Belgium is that, if there are multiple lanes, there is one highway-object with
lanes=2or there might be multiple traffic lights which actually have different properties (e.g. one traffic light to turn left)Comment from onion42 on 28 December 2025 at 19:21
@Pieter Vander Vennet As a heads-up; are you aware of the berlin community Verkehrswende and their tagging systems/proposals? They go into a ton of detail, not nessecarily crossings themselves, but maybe getting in touch with them could get something rolling
osm.wiki/Verkehrswende-Meetup/Gehwege
osm.wiki/Verkehrswende-Meetup/Radwege#/media/File%3ACrossing_junction_Hermannplatz.png