# Using buffer distance as portion of polygon variable width in QGIS

Is it possible to create a buffer of for example 3/4 of the polygons local width?

I have a polygon of river body, somewhere the river has 20 meters in width, somewhere 70 m. I need to extract the cunette, which varies based on the rivers width. So i need to create a negative buffer that is 3/4 of the current river body polygon width, so it copies its current geometry.

Any ideas on how to achieve it?

I basically need to extract only the inner 1/3 of the width like this

• do u have the river width information somewhere available (as attribute in your table) ? Mar 3, 2021 at 10:44
• Can you provide an example of your current data and what you are trying to produce? Because if you created a negative buffer of 3/4 of the rivers width, you'd get no area.
– Erik
Mar 3, 2021 at 11:08
• @snaileater There is no information on the width, the width is changing with each vertex, river body is irregular object, there can't simply be attributes with width Mar 3, 2021 at 11:18
• @Erik It's a polygon, not polyline, it has a geometry. Mar 3, 2021 at 11:20
• If you could create a central axis of the river polygon, you could use this to calculate the width of the river. Possible with PostGIS: postgis.net/docs/ST_ApproximateMedialAxis.html Apr 5, 2021 at 8:21

I'm sure someone else can come up with a more elegant solution using PostGIS or python, but here's something I cobbled up using regular tools that should work if you're only concerned with a few river polygons. I hope I understood the size you want your resulting polygon relative to the original:

• v.voronoi.skeleton to find centerlines

• clean up the "dangles" at both ends

• Simplify with appropriate tolerance distance (to reduce the number of transects)

• Transect with a length large enough to be wider than the river polygon, both sides

• Clip the transect layer with the river polygon

• Points along geometry with these parameters:

• Distance: $length * (1/3) • Start offset: $length * (1/3) + 0.05 (the +0.05 is to make sure the third point is not created)
• In the new point layer, add a field called something like Order and run field calculator on it with this expression which gives all adjacent points an incremental value:

if("distance" = minimum("distance",group_by:="TR_SEGMENT"),"TR_SEGMENT","TR_SEGMENT" + ((maximum("TR_SEGMENT")-"TR_SEGMENT")*2) + 1)

• Points to path on the point layer using the new field as the order

• Lines to polygons to get the result

Here's what the layers look like, it gives an idea of the process, with the result being the orange polygon. I don't think it could easily be turned into a model to deal with large polygon sets because it requires manually fixing the voronoi centerlines and simplifying it.

I think you could work on a raster based approach to solving your problem. What you ideally would like to have is some sort of inversed Euclidian Distance calculation (from the polygon's exterior towards its inside.

You could attempt this as followed, I haven't tested but can get you started:

• use Polygon to Lines tool to convert the river to a line segment.
• Use Euclidian distance tool to calculate the distance towards the outside.
• Use the river polygon shape as the mask or extent of the Euclidian Distance calculation.
• If needed, convert the Euclidian Distance Raster back to a polygon using the polygonize tool, after resampling and extracting desired distance from the internal river line.

There are some articles that try to attempt this that might be of use as well:

• Thanks, these tips are helpful, and so are the links. I think this is the way to do it Mar 3, 2021 at 12:09
• Hi where can I find the Euclidian Distance Calculation? Mar 7, 2022 at 9:10

Here's an approach based on medial disks:

• Densify
• Triangulate (via constrained Delaunay)
• Compute the circumcircle of every triangle to form a collection of disks
• Union all disks
• If width fraction < ~0.5, compute concave hull of disk union (since gaps can appear)

I need to do something similar, but I don't seem to find the Euclidean distance tool.

I have a polygon which represents a riverbed area and I would like to make a buffer based on the river width.

So I am wondering if the proposed procedure is suited to achieve what I am trying to do.

The buffer would be n*width with n an integer number I choose. If you look at the picture you see that the buffer would be larger at the width1 cross-section, and it would be thinner at the width2 cross-section.

I tried using the percentage buffer tool, but with no luck.

I also tried applying a buffer with a geometry expression such as

width($geometry)*2  but "width" is not a characteristic of "geometry", so no luck here either. Probably the best way to do this is to draw some cross-section (e.g. one each 10-50 m), calculate the width and build kind of a skeleton of your river as you do for instance in HECRAS. There is a way you can create a polygon with an inside buffer that reduces the river width based on a certain percentage directly based on the initial polygon shape, using QGIS expressions - either with Geometry generator or with Geometry by expressions (See here for details on both options). Entering the expression in a new Geometry-generator type symbol layer, from the initial light blue polygon the dark blue version is created: To achieve this, use the following expression. Based on your data, you might want to change the following values - with geometry generator, you can do this in real time to see how the result changes when adapting the values: • The expression is based on a simplified version of the polygon to reduce complexity, make calculation faster an resulting polygons more compat: in case of very detailed shapes for the river polygon with many vertices, this reduces the no. of vertices. You might set the tolerance (in meters/layer units) at the end of line 6 (here: 1 - for further simplification, increase the number, to have no simplification at all, set the value to 0). • change the value of 0.1 (end of line 16) to a distance significantly smaller than the minimal river width • change the value of 100 (line 22) to a value larger then the largest river width. Be sure to include the minus sign to get negative values! • change the value of 0.3 (8th last line) to change the size of the remaining polygon. 0.3 means that form both sides, it reduces the local width of the river by 30%, so for both sides = 30%+30% -> remaining 40% of the local width. make_polygon( make_line ( array_filter( with_variable( 'river', simplify($geometry, 1),
array_foreach (
generate_series (1,num_geometries( nodes_to_points (@river))),
with_variable (
'line',
make_line(
geometry_n (nodes_to_points (@river), @element),
intersection (
difference (
boundary(@river),
buffer (geometry_n (nodes_to_points (@river), @element), 0.1)
)  ,
make_line (
geometry_n (nodes_to_points (@river), @element),
project (
geometry_n (nodes_to_points(@river), @element),
-100,