Wednesday, 21 March 2018

qgis - Determine on which side of a line points are located


I have a point shapefile (point.shp) and a line shapefile (line.shp). I want to determine on which side of the line each point is located.


The best would be to use a pyqgis command to add a field to the point.shp with value 0 for left and 1 for right.


An alternative that would work is to determine the nearest distance between each point and the line with distance taking negative values for points located on the left and positive value for points located on the right of the line.


enter image description here



Answer



By using PyQGIS 2 (I supposed for your tag pyqgis) you can do that with 'closestSegmentWithContext' method from QgsGeometry and 'azimuth' method from QgsPoint. I used shapefile of following image:


enter image description here


to test below code:


from PyQt4.QtCore import QVariant


registry = QgsMapLayerRegistry.instance()

points = registry. mapLayersByName('points_grid')
line = registry. mapLayersByName('line')

position_field = QgsField( 'position', QVariant.Int)

points[0].dataProvider().addAttributes([position_field])
points[0].updateFields()


points_feats = [ feat for feat in points[0].getFeatures() ]
line_feat = line[0].getFeatures().next()

idx = points[0].fieldNameIndex('position')

points[0].startEditing()

for feat in points_feats:
pt = feat.geometry().asPoint()

point_in_line = line_feat.geometry().closestSegmentWithContext(pt)[1]
az = pt.azimuth(point_in_line)

if az > 0:
pos = 0
if az < 0:
pos = 1

feat[idx] = pos
points[0].updateFeature( feat )


points[0].commitChanges()

After running it at Python Console of QGIS, it was added 'position' field with value 0 for left and 1 for right as expected.


enter image description here


Editing Note:


The Process which requires more time is writing position values to respective field. To avoid that is preferable to use an enumerate list for points features and print point indexes (for left and right points) into a separate lists when the condition is met. Following code can do that.


from PyQt4.QtCore import QVariant

registry = QgsMapLayerRegistry.instance()


points = registry. mapLayersByName('random_points_900000')
line = registry. mapLayersByName('line')

points_feats = [ feat for feat in points[0].getFeatures() ]
line_feat = line[0].getFeatures().next()

idx = points[0].fieldNameIndex('position')

ids_0 = []

ids_1 = []

for i, feat in enumerate(points_feats):
pt = feat.geometry().asPoint()
point_in_line = line_feat.geometry().closestSegmentWithContext(pt)[1]
az = pt.azimuth(point_in_line)

if az > 0:
pos = 0
ids_0.append(i)


if az < 0:
pos = 1
ids_1.append(i)

print "Wait..."
print "Time: %s seconds " % timeit.timeit()

To test above code, I generate 900,000 random points and ran it. Result was produced in less than a second and, on average, each list has about 450,000 points as expected (see below image).


enter image description here



You can use indexes lists for producing memory layers with a QgsFeatureRequest or introduce them in point layer by using a more efficient method.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...