QGIS – How to Dynamically Style Polygons in a Vector Grid Based on Adjacent Polygon Values

qfieldqgisrule-basedspatial-query

I am trying to achieve a solution to display spatial relations in a vector grid using rule-based styling similar to the image below:
grid displaying rule based styling

I create the grid with the Vector>Research Tools>Create grid… option in QGIS. Then, I add any fields needed to the grid, i.e. "Count", to see the potential contamination of an area. The red squares display the count, and the green squares need to be assessed, while the orange squares are usually hidden. When I investigate now a green area, I enter the new count in the corresponding field, and if the number is greater than, i.e. 9, it should style any adjacent grid cells green.

I have already achieved working results with the following expressions (in the rule-based styling):

array_mean(overlay_intersects(@layer, Count > '9', limit := '8'))

and

array_mean(overlay_nearest(@layer, Count > '9', limit := '8', max_distance:='2'))

The problem is that these expressions are very processing heavy and can take a long time to process and render.

I also started looking into "Virtual Fields" within my attribute table, hoping that I could use a dynamically generated result as the basis for styling. I also achieved that with the following expression:

if(array_mean(overlay_nearest(@layer, Count > '9', limit := '8', max_distance:='2')), 'true', 'false')

And the following rule for rule-based styling:

 "Virtual Field name" =  'true' AND "Count" IS NULL 

The solution above works on smaller grids quite well, but grids of several thousand features overlay some of the areas we cover. Whenever I tried to create a "Virtual Field" containing an overlay function in a Grid with 5000+ features, my QGIS crashed. Also, I am concerned that if the "Virtual Field" creates, it would cause performance issues comparable to rule-based styling expressions).

We are running tablets with QField in the field, and to see areas of interest "on the fly" would help a lot. In addition, the dynamic styling would minimize user errors and only require entering one value into the assessed square. Up to now, we have entered additional values manually into the adjacent squares to change the styling.

I don't know if I am overcomplicating this or if it is just not possible to get what I am looking for with normal QGIS expressions.

Any suggestions or feedback?

Best Answer

The idea: Identify neighbours based on id, not on spatial relationship

To check spatial relationship in a large layer tends to slow down QGIS. So this idea is based not on a spatial relationship or just in an indirect way, using attribute values. As you created a regular grid, cells come with an id, normally starting in the top left cell, than going from top down and from left to right.

Thus neighboring cells can be easily identified based on their id. The difference between the id of the current cell and the ids of the cells to the left/right column or upper/lower row is constant and you can easily calculate it, based on the number of clumns/rows of the whole grid.

Implementation

Use layer style Geometry Generator to create the neighouring cells for the red cells.

  1. create an array with the id's of the neighboring 8 (for 9x9 neighborhood) cells: e.g. cell no. 38 (with value of 86) borders to cells 20, 21 and 22 to the left, 37 and 39 top/bottom and 54, 55, 56 to the right. So get the id's of the neighboring cells: array($id-18, $id-17,$id-16,$id-1,$id+1, $id+16,$id+17,$id+18).

  2. Use array_foreach() to get the feature for each of the neighboring ids and convert it to its geometry.

  3. Add an if() clause: this should run only for cells containing a value (= your red cells). In my case: num is not NULL. Otherwise, return an empty output: ''.

  4. Convert the elements of the array to a multipart geometry with collect_geometries().

Red cells contain a value in the attribute field num, all other cell contain just NULL. The expression selects the 8x8 neighbours of the red cells: enter image description here

Related Question