I have point layer, where points are grouped into groups - every feature has point_id and group_id. I want to generate around group of point buffer (for example 1500 meters) where front of points group is concave and back rounded ... like in the picture.

convex_hull(buffer(collect($geometry,group_by:="group_id"), 1500 ))

Any good idea how to make the front concave? I assume that in the feature attributes I have to specify (for example true/false) which points are front and which are back in specific group I'm using QGIS 3.28.15.

enter image description here

here is sample dataset,

link to sample dataset

in my real data set is thousands of points and hundreds of point groups, and the number of points in a one group can vary from 1 to 15 and front points can be also one-to-many

  • 3
    What do you call the front and the back of your groups? Are they alway 3 points layed out like you showed, or any number and randomly placed?
    – Kasper
    Commented May 1 at 16:50
  • there can be any number of point at one group ... usually 1 to 15 points
    – Rii Pii
    Commented May 2 at 6:05
  • Please add such information to your question. Something like that is essential to know to provide a good answer.
    – Matt
    Commented May 2 at 16:07

1 Answer 1


This expression makes a reasonable representation of the shape. It can be refined to produce a shape closer to the one you have drawn if needs be.

The expression assumes there is a boolean field called "front" to distinguish the points.

Bear in mind this was achieved using a Geometry Generator symbol layer. If you use Geometry by Expression processing tool, array_agg will need to be replaced by aggregate(@layer, 'array_agg', ...).

The output is a polygon geometry type.

-- the case statement is to render the final polygon only once*
case when @id = array_min(array_agg(@id, group_by:="group_id", filter:="front" is not null)) then

-- isolate the front points
with_variable('front_pnts', geometries_to_array(collect(@geometry, group_by:="group_id", filter:="front" = true)),

-- isolate the back point 
with_variable('back_pnt', geometries_to_array(collect(@geometry, group_by:="group_id", filter:="front" = false))[0],

-- make a line connecting the 2 front points
with_variable('connector', make_line(@front_pnts[0], @front_pnts[1]),

-- get the mid point between the 2 front points
with_variable('mid_point', line_interpolate_point(@connector, length(@connector) / 2),

-- make a line connecting the mid point and the back point
with_variable('connector2', make_line(@mid_point, @back_pnt),

-- make a new point at a specified distance along the line connecting the mid point and the back point (in this case 1/3 of the total length)
with_variable('new_point', line_interpolate_point(@connector2, length(@connector2) / 3), 
    -- `exterior_ring` is used in case the buffer distance is small enough to create a hole in the middle of the polygon
        -- create the buffered shape
            -- construct a preliminary MultiLine shape between the front and back points (see diagram at end of answer for clarification)
                make_line(@front_pnts[0], @back_pnt),
                make_line(@back_pnt, @front_pnts[1]),
                -- create a smoothed line between the two front points and the new point generated above
                    make_line(@front_pnts[0], @new_point, @front_pnts[1]), 
            -- this is the buffer distance. Here I use a relative distance (1/4 of the distance between the midpoint between the two 'front points' and the back point).
            -- an absolute distance can also be used of course.
            distance:=length(@connector2) / 4


-- close the case statement

* see this question and answer for explanation of the case statement.

enter image description here

Graphical explanation of the expression elements

enter image description here

  • what geometry type should be selected in gemetry generator module ... i prefefer polygon, because I want to fill diferent groups with different colors
    – Rii Pii
    Commented May 2 at 11:01
  • Indeed, the output is a polygon, and should be selected. I will add it to my answer.
    – Matt
    Commented May 2 at 15:52
  • 1
    It would be nice to have some feedback from the downvoter to clarify why they think this is a poor answer.
    – Matt
    Commented May 2 at 16:08
  • 2
    @RiiPii, then may I suggest leaving a comment and asking for some help to get it working, rather than downvoting. A seemingly unnecessary downvote with no explanation does not help improve answers. And the buffer dimension is clearly indicated in the expression. Refer to the comment that begins -- this is the buffer distance.
    – Matt
    Commented May 3 at 11:31
  • 1
    I answered based on the information in the question. I will take another look when I get time. Please add a link to some sample data. I don't want to have to guess what your data look like. For what it's worth, the only changes I made to the answer were to add some extra comments and a parameter name in the expression for extra clarification. The functionality remained exactly the same.
    – Matt
    Commented May 3 at 12:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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