Came across a question regarding how to determine the length along a line between two points, figured this wouldn't take too long in QGIS (it didn't, 10 minutes while twitting the steps) and decided to write this down since I'll probably forget I did it in a week or so.
The process can be divided logically into steps
1. Project/Snap the points to the line
2. Measure where each point is along the line
3. Clip the line between the locations
I was in the middle of another map and didn't bother to open a new project, so the line I drew is about the length of the US west coast, but it's CRS is EPSG:6991 which is Israel Grid (because I needed the result length in meters). For the example I drew 2 point beside the line, and not on it, because I didn't want to count on the points being snapped.
Snap the points to the line
This is relatively simple, we only need to do two actions here, get a specific point and snap it to the line. You can get the point by using either the get_feature or get_feature_by_id expression functions.
Your expression should look something like this
$geometry,
geometry(get_feature_by_id( 'points',1))
)
What I do there is grab the first point and check what is the closest place on the line to it, that's it for step 1.
If you want to see the projected points, you add a geometry generator symbol to the points and use the expression the other way around:
closest_point(
geometry(get_feature_by_id( 'line',1)),
$geometry
)
Brown points are the original, red are projected using the geometry generator |
Measure where each point is along the line
Now that we have the expression to create our points, we need to determine how far along the line each one is (we need that for step 3). The expression here is just wrapping the previous expression in another function, line_locate_point which measures just that.
line_locate_point(
$geometry,
closest_point(
$geometry,
geometry(
get_feature_by_id( 'points',1)
)
)
)
This returns a number, in this case, the length in meters from the line's start to the projected point.
Clip the line between the locations
Final step now, we use the projected points and the measurements, along with the line_substring function which takes 3 arguments, geometry, and distance from start of line for both points so our final expression will get us back a new geometry which is only the part of the line between both points.
We can use this expression either to draw the "clipped" line using the geometry generator, create a new layer with the Geometry By Expression processing tool, or just wrap it in the length function if we just want the length.
line_substring(
$geometry,
line_locate_point(
$geometry,
closest_point(
$geometry,
geometry(
get_feature_by_id( 'points',1)
)
)
),
line_locate_point( $geometry,
closest_point(
$geometry,
geometry(
get_feature_by_id( 'points',2)
)
)
)
)
This should be enough for most simple clips, you can notice that while the expression is long, it doesn't use a lot of functions or complex logic, in fact the most complicated part for you will probably be selecting the specific points and line you would want to use.
For some examples of selecting specific features using expressions you can check out my post about selections
UPDATE
The question was about getting a part of a route between points (so basically what this post teaches) and Babel expansion was:
"
First expression to connect points in a regular order, to be defined in line 4:
collect_geometries (
array_filter (
array_foreach (
generate_series (1,9), -- define here the list of $id's of the points to be included
line_substring(
$geometry,
line_locate_point(
$geometry,
closest_point(
$geometry,
geometry(get_feature_by_id ('points',@element))
)
),
line_locate_point(
$geometry,
closest_point(
$geometry,
geometry(get_feature_by_id ('points',@element+1))
)
)
)
),
@element IS NOT NULL
)
)
Second expression to connect points in any arbitrary order, to be defined in line 3:
with_variable ( 'array', array (17,10,4,3), -- define here the order in which the points should be connected collect_geometries ( array_filter ( array_foreach ( @array, line_substring( $geometry, line_locate_point( $geometry, closest_point( $geometry, geometry(get_feature_by_id ('points',@element)) ) ), line_locate_point( $geometry, closest_point( $geometry, geometry(get_feature_by_id ('points',array_get (@array,array_find (@array, @element)+1))) ) ) ) ), @element IS NOT NULL ) ) )
"
It's a great expansion and uses a variety of array and variable expression functions to solve this problem in an elegant way.
Comments
Post a Comment