I have a list of straight roads segmented which has a unique "ID". I would like to detect connected lines in the road network and make them in the same direction instead of reversing them one by one. So to simplify my question:

Please see the red arrow

Each line has a beginning point(x1, y1) and an ending point(x2, y2). For instance: if line A's beginning point(x1, y1) or ending point(x2, y2) is equal to line B's beginning point(x3, y3) or ending point(x4, y4), detect those lines and make it in the same direction. It doesn't matter which direction it is as long as they align in the same direction. Since there are thousands of lines, I need a smart way to do this all at once.

I can use the "Reverse direction of geometry" tool in QGIS but it doesn't resolve my issue because I have to do it one by one.

Can you give me some insights please either via any QGIS tool or Python code samples?

  • 1
    Does each line group have a unique ID? In your image the lines are colour coded, are these colours dictated by a certain attribute?
    – Cushen
    Commented Apr 12, 2022 at 2:03
  • Hi @Cushen, thank you for your reply. Yes, each line group has a unique ID which is segments. Example: RD009A, RD009B, RD009C. I categorized and colored by sections ID. In this case, section ID is: RD009. Commented Apr 12, 2022 at 2:15

2 Answers 2


Compare the azimuth of each line to the line group mean, and reverse the line in each group that deviate the most from the group mean:

Refactor fields (or use two field calculators) to calculate:

groupid = left(id, 5)

degrees(azimuth(start_point($geometry), end_point($geometry)))

Calculate a mean azimuth per group mean(expression:="azimuth", group_by:="groupid")

Calculate each lines deviation from mean deviation_from_mean = abs("azimuth"-"meanAzimuth")

Calculate a control field so we dont extract and reverse lines in the next step that shouldnt be reversed just because they deviate the most from the mean

control = case when deviation_from_mean>45 then 1 else 0 end

Extract the lines in each group that deviate the most from the mean deviation_from_mean = maximum(expression:="deviation_from_mean", group_by:="groupid") and control=1

Reverse the matching lines and merge with the none matching

enter image description here

  • Hi @BERA. Thank you for your brilliant solution. However, I couldn't understand what 45 represents in your equation here: control = "case when deviation_from_mean>45 then 1 else 0 end". Can you clarify it, please? Also when I implemented your solution it worked in the majority of the network but I still couldn't achieve the 100 percent expected outcome. Some of the same groupid didn't align in the same direction. Here is the link to the images: imgur.com/a/XjqvxZN Commented Apr 20, 2022 at 3:04
  • Hello. The 45 so you dont reverse the direction of one line segment in all groups. It has to be the line that deviates the most from the mean and where the deviation from the mean is more than 45 degrees. Try lowering it some and maybe get a better result
    – Bera
    Commented Apr 20, 2022 at 5:12

This is network task, so use networkx module. First compute connected components (groups) of undirected graph:

enter image description here

Select one dangling node per group and compute travel to them from other nodes:

enter image description here

Select lines where FROM node travel is less than TO node travel:

enter image description here

and flip selected:

enter image description here

Note: for true road network result will look controversial:

enter image description here

this is because from some point in the middle of 0 flow edges travel distance to destination is equal for 2 opposite directions at this point.

  • 1
    Thank you for such a cool tip with this Python package!
    – Taras
    Commented Apr 13, 2022 at 6:41
  • 1
    Can you show how you create the graph?
    – Bera
    Commented Apr 13, 2022 at 6:56
  • 1
    @BERA this is separate big topic. Perhaps ask question and I post a GIS workflow.
    – FelixIP
    Commented Apr 13, 2022 at 9:33
  • Thank you for your solution @FelixIP. Can you share the Python code for reference please? Commented Apr 20, 2022 at 3:21
  • There are 4 or more scripts in play here. So, no. I cannot share. And I don't want too.
    – FelixIP
    Commented Apr 20, 2022 at 5:02

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.