Layer 1 Line Layer 2 Polygon

The polygon will intersect the line in 2 places.


enter image description here

I'd like to attach the 2 distance values along the line to the polygon layer.

  • You could transform your Layer 2 polygon into lines using first the tool "Polygons to lines" then the SAGA tool "lines to points". Using this new layer you just created, toggle edit mode; get rid of all the points you don't want (depending on the size of your polygon of course), and perform a "distance to nearest hub - line to hub" on your two points. – Tim56 Feb 13 at 20:30
  • @Tim56 Thanks. Worth a shot, although my layer contains a very large number of polygons and the removal of lines may be too time consuming – Mardymonkey Feb 13 at 20:33

If you are open to using the Python console, I have written a PyQGIS script which should do what you are after.

Firstly, make sure that both your layers are in a projected coordinate system. If not, I suggest reprojecting them (right click on layer -> export -> save features as) and select a projected UTM CRS for your zone.

Secondly, please make a backup copy of your polygon layer just in case of unexpected results.

If you are not familiar with the Python console, just follow these steps (illustrated in the image below).

  1. Click the Python icon in the Plugins toolbar or press ctrl+alt+p to open the console.
  2. Click the notepad & pencil icon to open a new editor.
  3. Copy and paste the script below into the blank editor.
  4. Click the run icon to run the script.

The only changes you should need to make to the script is the layer names in lines 2 & 3 to make sure they match the names of your line & polygon layers.

enter image description here

Once you run the script, you should have 2 new numerical fields added to the attribute table of your polygon layer ('Distance_1' and 'Distance_2'). These will be populated with the distance along the line (in meters) from the start point of each line feature of the two intersection points with the polygon feature.

The script is not ideally optimized for one line transecting a series of abutting polygons, but I tested on a similar data set and it worked fine as seen in this screenshot:

enter image description here

Full script:

# Change layer names below to match your layers
line_layer_name = 'Line_layer_A'
poly_layer_name = 'Polygon_layer_2'
project = QgsProject().instance()
line_layer = project.mapLayersByName(line_layer_name)[0]
polygon_layer = project.mapLayersByName(poly_layer_name)[0]
lines = [f for f in line_layer.getFeatures()]
polygons = [f for f in polygon_layer.getFeatures()]
# Add distance fields to polygon layer; you can change length & precision values if you wish

flds = [f.name() for f in polygon_layer.fields()]
if 'Distance_1' not in flds and 'Distance_2' not in flds:
    flds = [QgsField('Distance_1', QVariant.Double, len=10, prec=3), QgsField('Distance_2', QVariant.Double, len=10, prec=3)]

def distance_to_point(feature, point):
    geom = feature.geometry()
    verts = [v for v in geom.constGet().vertices()]
    point, minDistPoint, nextVertex, leftOf = geom.closestSegmentWithContext(QgsPointXY(point))
    start_to_vert_after = geom.distanceToVertex(nextVertex)
    point_to_vert_after = minDistPoint.distance(QgsPointXY(verts[nextVertex]))
    distance_of_point_along_line = start_to_vert_after - point_to_vert_after
    return distance_of_point_along_line

#create a spatial index to speed up intersection search on large datasets
index = QgsSpatialIndex()
# get indexes of newly added distance fields
dist_1_idx = polygon_layer.fields().indexFromName('Distance_1')
dist_2_idx = polygon_layer.fields().indexFromName('Distance_2')

for p in polygons:
    distances = []
    test = index.intersects(p.geometry().boundingBox())
    candidates = [f for f in lines if f.id() in test]
    for f in candidates:
        ix = f.geometry().intersection(p.geometry())
        if ix:
            distance_1 = distance_to_point(f, ix.constGet()[0])
            distance_2 = distance_to_point(f, ix.constGet()[-1])
    if len(distances) == 2:
        atts = {dist_1_idx: distances[0], dist_2_idx: distances[1]}
        atts = {dist_1_idx: 0, dist_2_idx: 0}
    polygon_layer.changeAttributeValues(p.id(), atts)


Hopefully it works for you.

| improve this answer | |
  • Wow. Thanks. I think I have followed your instructions OK, but Im getting the following error. Traceback (most recent call last): File "C:\PROGRA~1\QGIS3~1.4\apps\Python37\lib\code.py", line 90, in runcode exec(code, self.locals) File "<input>", line 1, in <module> File "<string>", line 5, in <module> IndexError: list index out of range – Mardymonkey Feb 16 at 16:46
  • Hi @Mardymonkey. Hmm, if the error is coming from line 5 it sounds like your line layer name doesn't match. Did you edit the script? E.g. if your line layer is called District_roads, then line 2 in the script should read: line_layer_name = 'District_roads' with your layer name as a string (enclosed in quotes). Same for the polygon layer. – Ben W Feb 16 at 21:49
  • Yep, my error on that one. I now have this error though. File "<string>", line 42, in <module> – Mardymonkey Feb 17 at 7:54
  • Is there any more to the error message? It should say what type of error has been raised... – Ben W Feb 17 at 8:13
  • Traceback (most recent call last): File "C:\PROGRA~1\QGIS3~1.4\apps\Python37\lib\code.py", line 90, in runcode exec(code, self.locals) File "<input>", line 1, in <module> File "<string>", line 42, in <module> IndexError: list index out of range – Mardymonkey Feb 17 at 8:30

This is a solution created using the QGIS Field Calculator.

Assuming that you have more than one layer of polygons and lines, you need first of all add two new fields in the attribute table of your layers.

  1. in the line layer add a new field called "id" and populate it with the Field Calculator and the expression $id

  2. in the polygon layer add a new field called "line_id" and populate it with the id of the intersecting lines. You can use the expression

aggregate(layer:='line', aggregate:='concatenate', expression:=to_string($id), 
    filter:=intersects( $geometry, geometry(@parent))) 

With this two new fields that connect the layers you can use the following expression:

round(line_locate_point( geometry:=geometry(get_feature('line', 'id', "line_id")), point:=end_point(
intersection($geometry, (geometry(get_feature('line', 'id', "line_id"))))))))

This expression can be used in the polygon layer as Label > Value > expression... or used in the Field Calculator to create a new field.

This is the result:

enter image description here

ADDENDUM This expression shows the measure of the second point of intersection between polygons and line. If you want to have the first point just change in the expression from end_point to start_point.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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