# Generating single sided buffers on the same side of lines

I have a line and a point layers and trying to find points 100m to the left of each line.

The image below shows the single-sided buffer result. As can clearly be seen, one of the buffers is generated on the right side. But of course, it is the left side of the line direction.

How can I make sure the buffers are generated on the same side?

Here is the script I want to improve:

``````point_lyr = QgsProject.instance().mapLayersByName("POINTS")[0]
line_lyr = QgsProject.instance().mapLayersByName("LINES")[0]

i = 1
interval = 100 # meter

# Find all points to the left of each lines
for l in line_lyr.getFeatures():
buffer = l.geometry().singleSidedBuffer(interval, 1, Qgis.BufferSide.Left)

# find the points in the buffer

# assign the lane number (i)

i += 1
``````

You can use the fact that all lines are parallel and calculate the angle of the lines. Define a main angle (e.g. from the first line), and if a line does not fit that main angle draw the buffer on the otherside.

``````lines = iface.activeLayer()
interval = 250
for i,line in enumerate(lines.getFeatures()):
if i == 0:
myangle = line.geometry().angleAtVertex(0)
if line.geometry().angleAtVertex(0) == myangle:
buffer = line.geometry().singleSidedBuffer(interval, 1, Qgis.BufferSide.Left)
else:
buffer = line.geometry().singleSidedBuffer(interval, 1, Qgis.BufferSide.Right)

``````

You could add a tolerance if they are not perfectly parallel. E.g. by using `math.isclose()` with a relative or absolute tolerance:

``````import math
lines = iface.activeLayer()
interval = 250
tolerance = 11 # absolute, degrees for easier use
# you can also use a relative tolerance, e.g. math.isclose(a,b,rel_tol = tolerance)
for i,line in enumerate(lines.getFeatures()):
if i == 0:
myangle = math.degrees(line.geometry().angleAtVertex(0))
if math.isclose(math.degrees(line.geometry().angleAtVertex(0)), myangle, abs_tol = tolerance):
buffer = line.geometry().singleSidedBuffer(interval, 1, Qgis.BufferSide.Left)
else:
buffer = line.geometry().singleSidedBuffer(interval, 1, Qgis.BufferSide.Right)
``````

Or do some more fancy stuff like:

``````import math
import random
lines = iface.activeLayer()
buffers = QgsVectorLayer('Polygon?crs={}'.format(lines.crs().authid()), 'Buffers' , "memory")
buffers.dataProvider().addAttributes([QgsField("sourcefeatid", QVariant.Int), QgsField("lineangle", QVariant.Double), QgsField("mainaingle", QVariant.Double), QgsField("parallel", QVariant.String), QgsField("bufferside", QVariant.String)])
buffersize = 250 # in CRS units
tolerance = 22.5 # absolute; degrees for easier use; you can also use a relative tolerance, e.g. math.isclose(a,b,rel_tol = tolerance)
mainangle = None # define your mainangle here
bufferdirection = None # define a main-direction
# or do some fancy stuff to determine the bufferside
if not bufferdirection or bufferdirection not in ['west','east']:
bufferdirection = random.choice(['west','east'])

for i,line in enumerate(lines.getFeatures()):
polyline = line.geometry().asPolyline()
startpoint, endpoint = QgsPoint(polyline[0]), QgsPoint(polyline[-1])

if i == 0:
if mainangle is None:
mainangle = math.degrees(QgsGeometryUtils.lineAngle(startpoint.x(), startpoint.y(), endpoint.x(), endpoint.y()))
reversemainangle = (mainangle+180)%360

lineangle = math.degrees(QgsGeometryUtils.lineAngle(startpoint.x(), startpoint.y(), endpoint.x(), endpoint.y()))
print(lineangle)

if bufferdirection == 'west':
if 0 < lineangle <= 180:
print('Buffer left')
bufferside = Qgis.BufferSide.Left
else:
print('Buffer right')
bufferside = Qgis.BufferSide.Right
elif bufferdirection == 'east':
if 0 < lineangle <= 180:
print('Buffer right')
bufferside = Qgis.BufferSide.Right
else:
print('Buffer left')
bufferside = Qgis.BufferSide.Left

drawbuffer = True
if math.isclose(mainangle,lineangle,abs_tol=tolerance):
parallelattr = 'parallel'
print(parallelattr)
buffer = line.geometry().singleSidedBuffer(buffersize, 1, bufferside)
elif math.isclose(reversemainangle,lineangle,abs_tol=tolerance):
parallelattr = 'reverse parallel'
print(parallelattr)
buffer = line.geometry().singleSidedBuffer(buffersize, 1, bufferside)
else:
parallelattr = 'not parallel'
print(parallelattr)
buffer = line.geometry().singleSidedBuffer(buffersize, 1, bufferside)
drawbuffer = False

if drawbuffer:
with edit(buffers):
newbuffer = QgsFeature()
newbuffer.setAttributes([line.id(),lineangle,mainangle,parallelattr,str(bufferside)])
newbuffer.setGeometry(buffer)
• Not perfectly parallel. It worked after changing `if int(line.geometry().angleAtVertex(0)) == int(myangle):` Commented Apr 16, 2022 at 22:11