5

I'm trying to identify the coordinates of all of the intersections where a primary, secondary, or tertiary highway meets another primary, secondary, or tertiary highway. I searched in the API docs here but haven't been able to solve it yet. Here's what I have so far but clearly it returns all nodes associated with a primary secondary or tertiary road (I realize the second_road is redundant).

[bbox:{{bbox}}];
way[highway~"(primary|secondary|tertiary)"]->.first_road;
way[highway~"(primary|secondary|tertiary)"]->.second_road;
node(w.first_road)(w.second_road);
out;

I could take this data and reduce it on my end via javascript to find if a node shows up twice or more but I was wondering if there is a better way to do this via the Overpass query language?

2
  • 1
    I recommend to determine intersections locally (e.g. in Javascript). Doing so in Overpass QL would be extremly time consuming even for small areas.
    – mmd
    Apr 21, 2020 at 8:47
  • Thanks, thats precisely what I ended up doing :)
    – mpc75
    Apr 21, 2020 at 12:05

1 Answer 1

2

OpenStreetMap doesn’t explicitly tag intersections per se, but it does have tags representing traffic control at intersections, namely highway=traffic_signals, highway=stop, highway=give_way, and occasionally junction=uncontrolled. If you know the area is well-mapped for navigation, querying for nodes with these tags is the quickest and most straightforward option. Note that some mappers tag the stop bar (stop line) instead of the intersection node itself, which means you’d need to use an around filter to find the intersection node itself.


It’s also possible to query more comprehensively for all intersections, regardless of traffic control or whether traffic control happens to be mapped. The following queries should complete on the order of a few seconds within a neighborhood-sized bounding box, but they may not scale well beyond that.

Overpass v0.7.61 and above

Overpass v0.7.61 introduces more direct support for finding intersection nodes based on how many inbound way segments they connect:

[out:json][timeout:25][bbox:{{bbox}}];

// Major streets
way["highway"~"^(primary|secondary|tertiary)$"]->.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;

Since intersections aren’t tagged explicitly, they’re technically indistinguishable from the nodes that join two segments of a roadway. (A roadway can be split into two ways at any location for a variety of reasons or no reason at all.) As a workaround, this statement weeds out the insignificant nodes by requiring the two candidate streets to have different names.

To answer your question directly, I limited this query to primary, secondary, and tertiary roads. However, an intersection can also involve other highway classifications, for example an intersection at the foot of a freeway off-ramp (motorway_link meets tertiary) or at the beginning of a parking lot access road (primary meets service).

Overpass v0.7.59.5 and below

Prior to Overpass v0.7.61, it was necessary to start with multiple sets of streets and find their intersections:

[out:json][timeout:25][bbox:{{bbox}}];

// Major streets
way["highway"~"^(primary|secondary|tertiary)$"]->.streets;

// Get each intersection that connects two distinct streets
foreach .streets->.street(
  // Get adjacent roadways with the same name
  way.streets(around.street:0)(if: t["name"] == street.u(t["name"]))->.sameStreets;
  // Get distinct streets
  (.streets; - .sameStreets;)->.otherStreets;
  // Get nodes along the street and the other street
  node(w.street)->.streetNode;
  node(w.otherStreets)->.otherStreetsNodes;
  // Get nodes that connect the street with the other street
  node.streetNode.otherStreetsNodes;
  // Add these nodes to the result set
  (._ ; .intersections;)->.intersections;
);

.intersections out geom;

This bit needs some explanation:

// Get adjacent roadways with the same name
way.streets(around.street:0)(if: t["name"] == street.u(t["name"]))->.sameStreets;

In order for the (if:…) filter to compare tags between the two sets of streets, we have to get both sets into the same evaluation context. The trick is to take the larger set and filter it by proximity to the smaller set. (around:0) gets us the members of streets that have no distance from street, which are the ones we want to evaluate anyways.

This simple, name-based heuristic will still include any name change that occurs mid-block. To handle this and other edge cases, you’d need to consider the angle of each way at the intersection, but this is getting into the realm of routing graphs for routing engines.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.