How do I filter roads to include all those that are paved and have at least two lanes?

Hi! I am trying to filter roads in Overpass Turbo to select all those that are paved and have at least two lanes, but my code isn’t working properly. I’ve tried all the different combinations I can think of (see a few below) but there’s always one parameter that doesn’t seem to work properly. Any ideas as to what I could be doing wrong? Thank you!

1. Includes single lane, unpaved roads
[out:json][timeout:4000][bbox:{{bbox}}]; ( way["highway"] ["surface"!="unpaved"] ["lanes"!="^ [<2] $"]; ); (._;>;); out geom;

2. Missing at least one 2 lane road
[out:json][timeout:4000][bbox:{{bbox}}]; ( way["highway"] ["surface"!="unpaved"] ["lanes"~"^ [>=2] $"]; ); (._;>;); out geom;

3. Missing many 2 lane roads
[out:json][timeout:4000][bbox:{{bbox}}]; ( way["highway"] ["surface"!="unpaved"] (if:t["lanes"]>=2); ); (._;>;); out geom;

1 Like


// paved roads with more as 2 lanes 


out geom;

First of all, you don’t need a union with recurse down (._; >;); when you are using out geom , which already attaches coords to the way . It’s only required if you want the point features, eg =traffic_signals , =crossing , =stop_position .
You need to explain what’s meant by “missing”. What are you comparing to as the reference? Please remember many lanes=2 and surface= aren’t tagged yet. What location are you looking at? Need to know the situation to come up with defaults and assumptions. Another more advanced complication is dual-1-lane divided roads, or wrongly split ones.

There’s no universal advantage from (if:t["lanes"]>1) compared to (if:t["lanes"]>=2) . The meaning of decimal lanes=1.5 is unclear or can’t be assumed reliably, and depends on the requested definition. (Wider unmarked road vs Narrow marked 2-lane road)

Thank you for pointing that out; I am still new to OSM and appreciate your feedback. To answer your question, I can tell that some roads are missing based on looking at the map and being familiar with the area. This is in an urban area of California, and there were very clearly residential streets that were not included in the output, likely (as you mentioned) due to missing tags. Is there any way to get around that or will I always be missing roads if they don’t have those tags?

Also, you say there is no advantage to using (if:t["lanes"]>1) over (if:t["lanes"]>=2. Wouldn’t it be more advantageous, however, to use the former to include unmarked wider roads, or should I exclude this parameter altogether? The purpose of including lanes in the first place is to select roads that are of an approximate width and therefore hinder dispersal of a species I am studying. The number of lanes (often) relates to width and seems to be more widely used than the width parameter. Alternatively, I can assume that certain roads (e.g., motorway, trunk, primary, etc.) are of a certain width, which would also justify excluding lanes altogether. I noticed that the if statement excludes roads that do not have this tag, which is why I believe there were so many missing from my output with option #3.

Also, this is my updated code so far that seems to get me most of the roads I need. There are a few extra roads included (e.g., gravel roads in a marsh) that I figured I could edit out in another application if necessary. If you have any further suggestions for ways to improve my code, however, I would welcome your feedback.

[out:json][timeout:4000][bbox:{{bbox}}]; ( way["highway"~"^(motorway|trunk|primary|secondary|tertiary|residential)$"] ["surface"!="^(unpaved|compacted|gravel|dirt|ground)$"] ["lanes"!~"^(<2)$"]; ); out geom;

Thank you for your suggestion. I have updated the surface parameter to exclude compacted and ground as well.

["lanes"!~"^(<2)$"] has no effect. There’s no such regex syntax. It doesn’t do anything.
I would split them into different steps for clarity, and efficiency. This allows to easily combine different crtieria.

  way.all[lanes](if: t["lanes"] >=2);
  way.check[!"oneway"][!passing_places][junction!=roundabout][junction!=circular]; way.check["oneway"="no"][!passing_places];
(._; - way._["surface"]["surface"!="^(unpaved|compacted|gravel|dirt|ground)$"]);
out geom;

There’s still the smaller problem of a pair of highway= + lanes=1 + oneway=yes roadways forming a two-way road, eg Way: 905500656 | OpenStreetMap section of CA-12 with delineators. This is a 2+1 road on the east, and =motorway= + lanes=1 in general are mostly hov=designated , resulting in them dropped, but with a parallel highway= next to them. Therefore it again depends on the format and attributes you require.
Ideally, the best method is to not filter directly. These should be combined in the preprocessing of the raw data for accuracy and simplicity.