5

In my QGIS 3.10 Project, I have one polygon layer and one point layer.

Each of these layers has objects that belong to different categories based on some attributes: for simplicity, let's say RED and GREEN polygons and RED and GREEN points. The point layer also has a numeric attribute.

I'm trying to calculate, for each polygon, the mean value of the numeric attribute of the points that are within it if they are both of the same types. That is if the polygon is RED use only the RED points within it and ignore the GREEN ones, and the opposite case is if the polygon is GREEN.

I've tried using "Join attributes by location (summary)" and ``v.vect.stats` but I can't get the "same color rule" to work. Also tried creating a Virtual Layer but returns a lot of duplicated polygons.

New contributor
DieterP is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

3 Answers 3

7

Great question. Welcome to the weird and wonderful world of the aggregate() function. It's very powerful but tricky to learn, I've used it a lot and still have to read the documentation carefully most times I use it.

Add a new field to your polygon layer and populate it using the aggregate() function:

enter image description here

For this, it's useful to remember that the layer you are running this expression on is called the parent layer.

Let's run through the parameters:

  1. layer. This is the layer you are aggregating results from, the point layer, in my example, it's called 'point_layer'.
  2. aggregate. This is the type of aggregation you want to perform, for you it will be 'mean'
  3. expression. This is the expression you are aggregating (sorry not sure how else to explain it). In your case, it's simply your number field, "num_field" in my example.
  4. filter. This is how you are going to filter your results, otherwise, it would just return the mean for all points. You need to filter the points in two ways, the first filter being that they are intersected by the parent polygon, and the second filter being that their colour field ("point_colour_field" in my e.g.) matches the parent polygon's colour field ("poly_colour_field" in my e.g.).
  5. concatenator. Not needed.
  6. order_by. Not needed.

First filter: to check they intersect you use the intersects() function, this requires two input geometries. The first is the point geometry which is just $geometry, and the second is the geometry of the parent polygon which is called using geometry(@parent).

Second filter: The "point_colour_field" needs to equal the parent polygon "poly_colour_field". The polygon colour field is called using attributes(@parent)['poly_colour_field']

The filters are combined using and.


With all that said, this is the expression to use (with your own layer and field names substituted in of course):

aggregate(
    'point_layer',
    'mean',
    "num_field",
    intersects($geometry, geometry(@parent))
        and "point_colour_field" = attributes(@parent)['poly_colour_field']
    )
1
  • 2
    A great answer!
    – Babel
    Nov 23 at 17:32
3

There is a possibility of using a "Virtual Layer" through Layer > Add Layer > Add/Edit Virtual Layer...

select  poly.color, 
        poly.geometry, 
        avg(pnt.value) mean_pnt_val
from "poly1" poly
left join "Random points" pnt
on st_intersects(poly.geometry, pnt.geometry) and poly.color=pnt.color
group by poly.color, poly.geometry

enter image description here

2

Another approach is to use overlay_contains() to get all points inside a polygon.

  • Define a filter based on the criteria you need: To get the color of the current polygon inside the filter condition, create a variable @polycolor using with_variable().
  • To get the variable inside the filter condition, create the overlay_contains() function as a string, concatenating the elements with pipes ||.
  • To convert the string to a function, use eval().
  • The function overlay_contains() returns an array of the values of the field "number" from the point layer that meets the condition. To calculate the mean value, use array_mean().
  • "name_polygon" and "name_point" are the attribute fields in the respective layers.

Be sure not to confuse double quotes " with two single quotes '' and be sure to include all single quotes exactly as in the expression: one single quote ' (e.g. line 3), two single quotes '' (line 7) and three single quotes ''' (line 9).

array_mean(
    with_variable(
        'polycolor',
        "name_polygon",
        eval('
            overlay_contains (
                ''point'',
                "number",
                filter:="name_point"=''' ||  @polycolor  || '''
            )
        ')
    )
)

Your Answer

DieterP is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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