Calculating polygon's length along a line?

Using QGIS:

(1) Create point layer of the polygon vertices using Extract vertices tool (in the QGIS Processing Toolbox > Vector geometry).

enter image description here

(2) Open the attribute table of the newly created Vertices layer.

(3) Start the Field Calculator and;

(3A) Create a new field, let's call it min_poly to store the minimum distance and give an expression:

minimum(
 line_locate_point(
  geometry:=geometry(get_feature_by_id('Lines', '1')), point:=$geometry), 
 group_by:="fid")

NB: you will need to change 'Lines' to your actual line layername, and '1' to the line id you have in your attribute table.

(3B) Create another new field, let's call it max_poly to store the maximum distance and give an expression:

maximum(
 line_locate_point(
  geometry:=geometry(get_feature_by_id('Lines', '1')), point:=$geometry), 
 group_by:="fid")

enter image description here

Since the Vertices layer keeps fid field from the original polygon ids, you will find min_poly and max_poly is the minimum/maximum distance along the line for each polygon.

(The above example shows the first polygon fid=1 spans from 8.688 to 24.45, while the second polygon fid=2 is from 35.062 to 42.496).


I don't know how to do it with QGIS or ArcGIS but what you want feels like a width of an oriented bounding box. As a proof of concept I rotated your sample image so that the projection line is horizontal.

enter image description here

Then I digitized the polygons from the image and generated envelopes for them. Width of the envelope answers your question.

enter image description here

What is missing is a tool that creates bounding box at a given angle. QGIS does have Oriented minimum bounding box tool but user can't give a fixed angle for that. Probably you could adopt the PostGIS solution from the accepted answer to this question Creating "oblique bounding box" with maximum width/height ratio?.


Based on @kazuhito's answer I put together a single, hacky expression in the QGIS Field Calculator that should do the same thing in one step.

However I can imagine this will be very resource-intensive on larger datasets. I think the problem is best suited to a Python implementation, which obviously handles referencing and iteration far better than the Field Calculator.

array_last(array_sort(array_foreach(
generate_series(1,num_points($geometry)-1),
line_locate_point(aggregate('lines','collect',$geometry),
point_n($geometry,@element))),1)) 
- array_first(array_sort(array_foreach(
generate_series(1,num_points($geometry)-1),
line_locate_point(aggregate('lines','collect',$geometry),
point_n($geometry,@element))),1))

This first creates an 'array' of node numbers using generate_series(), specifying the maximum as the number of nodes in each polygon - this is num_points($geometry), minus 1 to skip the repeated first/last node.

You can then pass the values of this array through a function to generate another array using array_foreach(). Here we pass the polygon node number (represented as @element) to point_n(), which returns the actual geometry of that node, then we feed that into line_locate_point() to determine its length along the specified line (see Important Note below).

The resulting array is then sorted in ascending order using array_sort() which then lets us get the "leftmost" and "rightmost" distances along the line using array_last() and array_first(). Subtract the two and the result is the "length" of the polygon along the line.

See below for an example of the above expression shown as a label in the polygons (plus "leftmost" and "rightmost" line distances split off from the above expression). For comparison I have also included extracted vertices and relevant line distance values. Green vertices are the "leftmost" and "rightmost" vertices along the line. Note the top-left polygon where the green point is actually further along the line than the point to the right below it, due to the angle of the line...

Important Note:

The line layer geometry is referenced here using aggregate(). You will need to change the layer name ('lines') as required, and if you have multiple lines, you should add a filter to specify which line you want to compare it with, e.g.: aggregate('lines','collect',$geometry,"name"='TrainLine1') . To make this automatically work on the closest line I really recommend SQL or Python over Field Calc.

Also, this calculates the "length" of the Polygon ALONG the line, including if the line is kinked per my example. If you want the straight-line distance... maybe calculate the distance() between the relevant nodes?

enter image description here