Exploring variables in QGIS pt 3: layer level variables

In part 3 of my exploration of variables in QGIS 2.12, I’m going to dig into how variables are scoped in QGIS and what layer level variables are available (you can read parts 1 and 2 for a general introduction to variables).

Some background

Before we get to the good stuff, a bit of background in how variables work behind-the-scenes is important. Whenever an expression is evaluated in QGIS the context of the expression is considered. The context is built up from a set of scopes, which are all stacked on top of each other in order from least-specific to most-specific. It’s easier to explain with an example. Let’s take an expression used to set the source of a picture in a map composer. When this expression is evaluated, the context will consist of:

  1. The global scope, consisting of variables set in the QGIS options dialog, and other installation-wide properties
  2. The project scope, which includes variables set in the Project Properties dialog and the auto-generated project variables like @project_path, @project_title (you can read more about this in part 2)
  3. composer scope, with any variables set for the current composer, plus variables for @layout_pagewidth, @layout_pageheight, @layout_numpages, etc.
  4. composer item scope for the picture, with item-specific variables including @item_id

The more specific scopes will override any existing clashing variables from less specific scopes. So a global @my_var variable will be overridden by an @my_var variable set for the composer:

overridden

Another example. Let’s consider now an expression set for a data-defined label size. When this expression is evaluated the context will depend on where the map is being rendered. If it’s in the main map canvas then the context will be:

  1. The global scope
  2. The project scope
  3. map settings scope, with variables relating to how the map is being rendered. Eg @map_rotation, @map_scale, etc
  4. layer scope. More on this later, but the layer scope includes layer-level variables plus preset variables for @layer_name and @layer_id

If instead the map is being rendered inside a map item in a map composer, the context will be:

  1. The global scope
  2. The project scope
  3. The composer scope
  4. An atlas scope, if atlas is enabled. This contains variables like @atlas_pagename, @atlas_feature, @atlas_totalfeatures.
  5. composer item scope for the map item
  6. map settings scope (with scale and rotation determined by the map item’s settings)
  7. The layer scope

Using layer level variables

Ok, enough with the details. The reason I’ve explained all this is to help explain when layer level variables come into play. Basically, they’ll be available whenever an expression is evaluated inside of a particular layer. This includes data defined symbology and labeling, field calculator, and diagrams. You can’t use a layer-level variable inside a composer label, because there’s no layer scope used when evaluating this. Make sense? Great! To set a layer level variable, you use the Variables section in the Layer Properties dialog:

Setting a layer variablee

Setting a layer variable

Any layer level variables you set will be saved inside your current project, i.e. layer variables are per-layer and per-project. You can also see in the above screenshot that as well as the layer level variables QGIS also lists the existing variables from the Project and Global scopes. This helps show exactly what variables are accessible by the layer and whether they’ve been overridden by any scopes. You can also see that there’s two automatic variables, @layer_id and @layer_name, which contain the unique layer ID and user-set layer name too.

Potential use cases for layer-level variables

In the screenshot above I’ve set two variables, @class1_threshold and @class2_threshold. I’m going to use these to sync up some manual class breaks between rule based symbology and rule based labeling. Here’s how I’ve set up the rule-based symbols for the layer:

Rule based symbology using layer level variables

Rule based symbology using layer level variables

In a similar way, I’ve also created matching rule-based labeling (another new feature in QGIS 2.12):

Matching rule-based labels

Matching rule-based labels

Here’s what my map looks like now, with label and symbol colors matched:

*Map for illustrative purposes only... not for cartographic/visual design excellence!

*Map for illustrative purposes only… not for cartographic/visual design excellence!

If I’d hard-coded the manual class breaks, it would be a pain to keep the labeling and symbology in sync. I’d have to make sure that the breaks are updated everywhere I’ve used them in both the symbology and labeling settings. Aside from being boring, tedious work, this would also prevent immediate before/after comparisons. Using variables instead means that I can update the break value in a single place (the variables panel) and have all my labeling and symbols immediately reflect this change when I hit apply!

Another recent use case I had was teaming layer-level variables along with Time Manager. I wanted my points to falloff in both transparency and size with age, and this involved data defined symbol settings scattered all throughout my layer symbology. By storing the decay fall-off rate in a variable, I could again tweak this falloff by changing the value in a single place and immediately see the result. It also helps with readability of the data defined expressions. Instead of trying to decipher a random, hard-coded value, it’s instead immediately obvious that this value relates to a decay fall-off rate. Much nicer!

I’m sure there’s going to be hundreds of novel uses of layer-level variables which I never planned for when adding this feature. I’d love to hear about them though – leave a comment if you’d like to share your ideas!

One last thing – the new “layer_property” function

This isn’t strictly related to variables, but another new feature which was introduced in QGIS 2.12 was a new “layer_property” expression function. This function allows you to retrieve any one of a bunch of properties relating to a specific map layer, including the layer CRS, metadata, source path, etc.

This function can be used anywhere in QGIS. For instance, it allows you to insert dynamic metadata about layers into a print composer layout. In the screenshot below I’ve used expressions like layer_property(‘patron’,’crs’) and layer_property(‘patron’,’source’) to insert the CRS and source path of the “patron” layer into the label. If either the CRS or the file path ever changes, this label will be automatically updated to reflect the new values.

Inserting dynamic layer properties into a composer label

Inserting dynamic layer properties into a composer label

 

So there you go – layer level variables and the layer_property function – here in QGIS 2.12 and making your workflow in QGIS easier. In the final part of this series, we’ll explore the magical @value variable. Trust me, I’ve saved the best for last!

Tagged , , , ,

17 thoughts on “Exploring variables in QGIS pt 3: layer level variables

  1. Leehan says:

    very nice job and blog !
    I can’t wait for the “magical @value variable” !

  2. Valerie Anderson says:

    Thank you so much, Nyall.

  3. Cyril Sauvenay says:

    Hi
    Thanks very much for your explanations, which are very useful!
    Maybe can you help me: when trying to use variables as you showed (put a font in file variable) it doesn’t work: the font described in attributes is taken in account by QGIS, not the one described in the variable (even the most simple one, like Times, etc.)
    Could it be a bug?
    Thanks again for your help!
    Best regards
    CS

    • Nyall Dawson says:

      Hi Cyril – just to confirm, you’ve set a data defined override for the label font to the created variable?

      • Cyril Sauvenay says:

        Hi Nyall
        Not sure that you received my answer, a few weeks ago.
        Maybe I don’t understand your question… I did what you showed: enter a label in a variable created for the file. But this label is not taken in count when I chose the variable in the label characteristics of the layer.
        Thanks again!

  4. Phil I. says:

    Hi Nyall,
    Awesome blog.

    Do you know if there a list of variables and how they are used?

    Regards,
    Phil

  5. Marcin says:

    Hi, will there be the next part of the series ?

  6. george vella says:

    hi

    splendid!

    can one use variables, eg. project, in the query builder filter expressions?

    regards

  7. Tomasz R. says:

    Hi Nyall,
    it was really good to read your explanation of using variables in qgis. Now I know how to do it and it helps me a lot while managing my project.
    The problem I have is that variables which I set up are not read in QGIS-Web-Client or any other QGIS Server based service (e.g. WMS). Is there something special that I have to change in settings or do I have to get used to it?

    Regards

  8. Charles says:

    Variable are very useful. To be able to modify them directly in a panel (like the style panel works for the style) could be very useful to because it takes some time to go constantly in the project properties.

    Thank you for your work.

    Regrads.

  9. Awesome Coverage on Variables. I am curious how one can copy project and composer level variables from one QGIS project to another?

    Or even more importantly how one could set default project/default composer/default layer level variables when ever new content is created.

    Obviously important from a global customisation point of view..

  10. This series has been just great – looking forward to Part 4! This @value has me intrigued.

  11. Gui says:

    Thanks Nyall, very usefull

  12. Saijin_Naib says:

    I’m glad I found your blog. After 11 years of using ArcGIS for academia, I’m having a bit of a time wrapping my brain around QGIS, but I like what I’ve found thus far (with the exception of how arbitrary units can’t be specified for tools easily [buffer!]).

    I see some really great power in how you can establish variables that are set to be used elsewhere in the program. Along those lines, I’d like to set a few variables around the datetime module.

    For instance, I have this:
    datetime_year = @year(now())

    It does not return the current year as expected, but simply the str() ‘now()’.

    What am I not getting?

    Thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *