Exploring The QGIS Expression Engine, Part 4: Selecting By Attributes And Location With One Expression

 I recently encountered a Facebook post asking whether any GIS software can select by both attributes and location with one tool. My answer:

        Yeah, wanna guess which software?

To which the person answered, knowing me, "QGIS with python🐍?", to which I clarified, you don't need python for that and referred them to my second post about the expression engine: Exploring The QGIS Expression Engine, Part 2: What's Missing From Select By Location, in it I perform both a spatial selection based on an attribute selection inside one expression (I select all the countries intersecting a 3 degree buffer from the Netherlands). Now that was a relatively complex expression since it used a reference geometry from within the same layer. 

An expression using a features geometry can be used just as easily as using the attributes from its table. For example, using the same Natural Earth Countries layer, if you would want to select a country by name or continent your expression would look like this:

     "NAME" =  'Angola'  or  "CONTINENT" = 'Europe' 

 


But what if you want to select using the geometry of a country?

We can use either the geometry itself, and interpolate it to get a specific node, the centroid, the X/Y coordinate or a derived attribute like length for lines or area for polygons. Lets use a derived attribute and select all countries larger than 1,000,000 square kilometers.

    ($area/1000000) > 1000000

Looks pretty simple, diving by 1,000,000 to get square kilometers and selecting using that number.

Now lets try something a bit more complex. lets interpolate geometry, and check which countries have a centroid which is west of the prime meridian. now lets break this process down to how we do that.
 

1. get the geometry of each feature:  $geometry -> returns geometry

2.  get the centroid of the geometry: centroid($geometry)
-> returns geometry

3. get the X coordinate of the centroid:  x(centroid($geometry)) -> returns number

So any x coordinate west of the prime meridian would be smaller than 0 since x coordinates increase going east and decrease going west of the prime meridian. our final expression looks like this:

        x(centroid($geometry)) < 0


 

See how simple that was? if we wanted to use the geometry of a specific feature from another layer we could use a function like get_feature which takes three parameters, layer, attribute and value which allows you to select a feature by the value of one of its attributes.

When we have a feature from another layer instead of the geometry of our layer we can use the geometry(feature) function instead of the $geometry attribute. you can also use that on the same layer to use a specific feature.

Lets use all of this to get all the European countries that touch (share a border) with Russia and have a population larger than 10,000,000

1. first we get the feature for Russia: get_feature('Countries_50m','NAME','Russia') -> returns a feature

2. now extract the geometry of that feature: geometry(get_feature('Countries_50m','NAME','Russia')) -> returns a geometry

3. now lets check who touches that geometry: touches(geometry(get_feature('Countries_50m','NAME','Russia')),$geometry) -> returns true\false as 1 or 0

 


4. now we just add the attribute selection for continent: touches(geometry(get_feature('Countries_50m','NAME','Russia')),$geometry) and "CONTINENT" = 'Europe' 

This return true or false for each country, when true that country would be added to the selection so out of the 14 countries that touch Russia, only 8 are counted as European.

 

5. and finally  we select only those countries with a population larger than 10,000,00: touches(geometry(get_feature('Countries_50m','NAME','Russia')),$geometry) and "CONTINENT" = 'Europe' and  "POP_EST" > 10000000

We are now only left with Ukraine and Poland, using one spatial condition and 2 attribute table conditions.


And that's it, all you have to do is break down your expression so using multiple functions in a row don't look as intimidating and you can get results very quickly selecting by both attribute and location.


previous posts about the expression engine:

Exploring The QGIS Expression Engine, Part 1: Getting Values From JSON & HSTORE

Exploring The QGIS Expression Engine, Part 2: What's Missing From Select By Location 

Exploring The QGIS Expression Engine, Part 3: Writing Custom Expression Functions

Comments