# Creating fixed length segment of line around point on line

I've a layer of rivers as lines, split in segments by their size. I've created random points on those lines and selected some of them - 90 in total.

I'd like to take those selected points on lines, set them as midpoints, and get a segments of 5 km upstream and 5 km downstream from the given points on rivers. I managed to split lines at points but that doesn't give me much.

Some of the segments will be shorter than 10 km as some rivers are small and flow in the sea. On some those points are close to sea while the river is big enough to shift the 10 km segment upstream.

Is this doable or manual is the only option here to achieve this?

You can run "Service Area (From Layer)" from processing toolbox.

• Choose your river layer as input (make sure it is in a metric CRS)
• Choose "Shortest" as path type to calculate
• And your points as Vector layer with starting points (make sure it is in the same metric CRS as your river layer)
• As Travel cost enter 5000
• If needed, you can also use the advanced options and e.g. add a stream direction

It will trace lines along your river network up to 5km from each point as single lines (so you get 90 output features). Example:

If you need each direction from a point as single feature (so 180 in total), you can run "Split lines with points" from SAGA processing tools to split the result.

You can achieve this using a virtual layer.

It would compute what 5km means in terms of fraction of the line length. Then, it will identify at which fraction of the line length is the point located. It will look at the difference between 1.0 (full length) and the expected line fraction for the extrapolated point after the point. If less than 1, the ending fraction (point) is kept. If more than 1, the endpoint is kept and the difference is added to the start point, which is now located at `current point fraction - fraction of 5km - difference between 1 and end point fraction`. To avoid going before the start (0.0), another comparison is done.

At last, once the starting and ending fractions have been identified, a line substring is created. The resulting line will always be 10km long, centered on the mid point as much as possible (except if there are less than 5km from the start, though this can be changed easily)

go to the menu `Layer > Add Layer > Add/Edit Virtual Layer...` and enter the following query. Replace `pointloc` and `river` by your true layer name, and feel free to keep any field from either layer (like `p.id` and `l.id`).

The 2 first computed fields (fromPC and toPC) and here for information only, you can safely remove them.

``````select p.id, l.id,
ST_Line_Locate_Point(l.geometry,p.geometry) - 5000.0/ st_length(l.geometry) + min(0,1.0-(ST_Line_Locate_Point(l.geometry,p.geometry) + 5000.0/ st_length(l.geometry))) fromPC,
min(1.0,ST_Line_Locate_Point(l.geometry,p.geometry) + 5000.0/ st_length(l.geometry)) toPC,
st_line_substring(l.geometry,ST_Line_Locate_Point(l.geometry,p.geometry) - 5000.0/ st_length(l.geometry) + min(0,1.0-(ST_Line_Locate_Point(l.geometry,p.geometry) + 5000.0/ st_length(l.geometry))),min(1.0,ST_Line_Locate_Point(l.geometry,p.geometry) + 5000.0/ st_length(l.geometry))) as geometry
from pointloc p
join  river l
on st_intersects(l.geometry,p.geometry)
``````

You can do this in two steps:

1. Split the lines at your points.

2. Use an expression to create geometries (see here for details) using the function `line_substring`.

In detail:

1. First combine all connected lines to one feature (river) if that is not yet the case. Than split the lines with one of these methods or use the following procedure: convert the points to small lines that you can than use for splitting. Convert the points to lines with Geometry by expression this expression: `extend( make_line( \$geometry, project( \$geometry, 1,0 )), 1, 0 )`. Than use `Menu Processing / Toolbox / Split with lines` - use the rivers layer as input and the created lines as `Split layer`.

2. Than you just have to create your lines using the following expression (explanation how the expression works below):

``````with_variable (
'dist',
15000,
combine(
if (
intersects (
start_point (\$geometry),
aggregate( 'points', 'collect', \$geometry)
),
line_substring (\$geometry, 0, @dist),
make_line (centroid(\$geometry), centroid(\$geometry))
),
if (
intersects (
end_point (\$geometry),
aggregate( 'points', 'collect', \$geometry)
),
line_substring(
\$geometry,
length(\$geometry)-@dist,
length(\$geometry)
),
make_line (centroid(\$geometry), centroid(\$geometry))
)
)
)
``````

Screenshot: blue river-line, red river sections with length=5000 to both sides of the white dots:

Explanation: You have to create two lines with `line_substring`, one for each direction (starting from the point): towards the mouth and towards the source of the river. For the direction running in the same direction as the line, you can use the following expression: `line_substring (\$geometry, 0,15000)`, for the other direction: `line_substring(\$geometry,length(\$geometry) - 15000, length(\$geometry))`. However, the first and last section of the line will be considered two - so I added an `if`-condition: a line should be drawn only if the start-point of the current line feature intersects with one of the points: you only want these distance-line where you have points. As `result_when_false` argument, I first used `NULL` - this creates a valid output, however when you combine the two lines (towards mouth and towards source) this reason, for `result_when_false`, I created a 0-length line (line from `centroid(\$geometry)` to `centroid(\$geometry)`). Now you can combine both sides with `combine()`. To have more flexibility with playing around with different length, I added a `with_variable()` function: now just change the value on line 3 to get outputs for different lengths.

• Thanks everyone for answers! I liked the Service area option, while the virtual layer didn't work at all for some reason. The big issue I'm facing is the tributaries. Some points are close to the points where two or more rivers join. All the automatic options see all of them equal, so the resulting segments are all over the many tributaries. There's no option to include in the query a request the selection to follow only the biggest stream, defined in feature properties, is there?
– edge
Apr 1, 2021 at 13:11
• I tried the virtual layer solution as well, it works. In the initial version, there was a comma missing at the end of line 1. In the version you find now, it should work. Service area is probably indeed the easiest solution. For the tributaries, you could just consider dis-connecting the lines (creating a small gap), so no continuing line can be found there. More a hack/workaround, though. Apr 1, 2021 at 13:18

What worked best for me in the end was the extraction of line segments using buffer zones.

1. Open "Buffer" from Processing Toolbox and choose a distance of 5000 m:

1. Go to Vector->Processing->Intersection and select your river layer as Input Layer and the created Buffer layer as overlay layer:

1. This will create a layer with the described line segments of 10 km each around the midpoint. This output layer is also useful for further geometry calculations (length, sinuosity,...). I mention this because the above "Service Area (From Layer)"-solution didn't return the correct length in my case.
• Indeed also a solution for a slightly modified question: the line lenght itself will not be the same as the buffer size (so not 5000m) unless the lines are absolutely straight from the center. Jun 8, 2021 at 16:49

Use QGIS expressions to generate sections along the river of a certain distance.

Use the following expression with Geometry by expression or Geometry generator (see here for details about these two options) on the point layer. You only have to replace `rivers` (line 3) with the name of your line layer:

``````with_variable (
'river',
array_first (overlay_nearest('rivers',\$geometry)),  -- replace rivers with the name of your line layer
union (
line_substring (
@river,
line_locate_point( @river, \$geometry)-5000,
line_locate_point( @river, \$geometry)
),
line_substring (
@river,
line_locate_point( @river, \$geometry),
line_locate_point( @river, \$geometry)+5000
)
)
)
``````

The expression in action, here with Geometry generator: from the white dots, a red line along the river (blue) with line of 5000 m is generated to both sides of the point: