6

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.

enter image description here

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 

1 Answer 1

7

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) buffers.dataProvider().addFeature(newbuffer) QgsProject.instance().addMapLayer(buffers) 
1
  • 4
    Not perfectly parallel. It worked after changing if int(line.geometry().angleAtVertex(0)) == int(myangle): Commented Apr 16, 2022 at 22:11

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.