{"id":2245,"date":"2017-05-10T10:42:41","date_gmt":"2017-05-10T08:42:41","guid":{"rendered":"https:\/\/www.opengis.ch\/?p=2245"},"modified":"2020-04-29T16:05:13","modified_gmt":"2020-04-29T14:05:13","slug":"best-practices-for-writing-python-qgis-expression-functions","status":"publish","type":"post","link":"https:\/\/www.opengis.ch\/it\/2017\/05\/10\/best-practices-for-writing-python-qgis-expression-functions\/","title":{"rendered":"Best practices for writing Python QGIS Expression Functions"},"content":{"rendered":"<p>Recently there have been some questions and discussions about python based expression functions and how parameters like <span class=\"lang:default decode:true crayon-inline \">usesGeometry<\/span>\u00a0 need\u00a0to be used. So I thought I&#8217;d quickly write down how this works.<\/p>\n<h1>There is some intelligence<\/h1>\n<p>If the geometry or a column is passed in as a parameter you do not need to request\u00a0it manually, you can even specify explicitly that you do not require the geometry or a column here.<\/p>\n<pre class=\"lang:python decode:true\">@qgsfunction(args='auto', group='Custom', usesGeometry=False, referencedColumns=[])\ndef my_buffer(geom, distance, feature, parent):\n\u00a0 \u00a0 return geom.buffer(distance, 5)<\/pre>\n<p>We can still call it within an expression by writing<\/p>\n<pre class=\"lang:pgsql decode:true\">my_buffer($geometry, \"impact_radius\")<\/pre>\n<p>The expression engine will do the appropriate thing and request the geometry and attributes automatically.<\/p>\n<h1>Hardcoded parameters<\/h1>\n<p>We can also write the function the following way. The difference is, that we will only ever be able to use it for this layer because it&#8217;s not portable. But sometimes there might be a good reason for doing that.<\/p>\n<pre class=\"lang:python decode:true\">@qgsfunction(args='auto', group='Custom', usesGeometry=True, referencedColumns=['impact_radius'])\ndef impact_radius_buffered_geometry(feature, parent):\n    if feature.geometry():\n    \u00a0 \u00a0 return feature.geometry().buffer(feature['impact_radius'], 5)\n    else:\n        return None<\/pre>\n<p>Notice that the geometry and columns were mentioned in two places. The decorator (<span class=\"lang:default decode:true crayon-inline \">usesGeometry=True<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline\">referencedColumns=[&#8216;impact_radius&#8217;]<\/span>) as well as within the function body with <span class=\"lang:default decode:true crayon-inline \">feature.geometry()<\/span>\u00a0 and <span class=\"lang:default decode:true crayon-inline \">feature[&#8216;impact_radius&#8217;]<\/span>\u00a0.<br \/>\nAlso notice that it was checked if the feature actually does have a geometry. with if <span class=\"lang:default decode:true crayon-inline \">feature.geometry()<\/span>\u00a0. It&#8217;s a common pitfall, that sometimes features with a NULL geometry suddenly make expression functions fail. It&#8217;s very easy to oversee this in development and then hard to track down in a production environment. Better stay on the safe side.<br \/>\nWhen you call this from an expression, you will call it the following way.<\/p>\n<pre class=\"lang:python decode:true\">impact_radius_buffered_geometry()<\/pre>\n<h1>Require all attributes<\/h1>\n<p>Sometimes it&#8217;s required to actually make sure that you have all attributes available. In this case you can specify\u00a0<span class=\"lang:python decode:true crayon-inline\">[QgsFeatureRequest.ALL_ATTRIBUTES]<\/span>.<br \/>\nThe following expression generates a list of all attributes of a feature, separated by a <span class=\"lang:default decode:true crayon-inline \">,<\/span>\u00a0. For this it obviously requires access to all attributes:<\/p>\n<pre class=\"lang:default decode:true \">@qgsfunction(args='auto', group='Custom', referencedColumns=[QgsFeatureRequest.ALL_ATTRIBUTES])\ndef concat_attributes(feature, parent):\n    my_string = ', '.join([str(attr) for attr in feature.attributes()])\n    return my_string<\/pre>\n<h1>Break it down<\/h1>\n<ul>\n<li>If you don&#8217;t hardcode attributes or the geometry inside your function, specify <span class=\"lang:default decode:true crayon-inline \">usesGeometry=False, referencedColumns=[]<\/span>\u00a0. As a rule of thumb, prefer to do things this way, this makes it easier to reuse functions in the future.<\/li>\n<li>If you do hardcode geometry or attributes, specify this manually.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Recently there have been some questions and discussions about python based expression functions and how parameters like usesGeometry\u00a0 need\u00a0to be used. So I thought I&#8217;d quickly write down how this works. There is some intelligence If the geometry or a column is passed in as a parameter you do not [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_themeisle_gutenberg_block_has_review":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[32,14,15],"tags":[125],"class_list":["post-2245","post","type-post","status-publish","format-standard","hentry","category-expressions","category-python","category-qgis","tag-qgis-org"],"jetpack_featured_media_url":"","jetpack-related-posts":[{"id":2235,"url":"https:\/\/www.opengis.ch\/it\/2017\/05\/02\/qgis-expressions-engine-performance-boost\/","url_meta":{"origin":2245,"position":0},"title":"QGIS Expressions Engine: Performance boost","author":"Matthias Kuhn","date":"2 Maggio 2017","format":false,"excerpt":"Expressions in QGIS are more and more widely used for all kinds of purposes. For example the recently introduced geometry generators allow drawing awesome effects with modified feature geometries on the fly. The last days at the QGIS developer meeting 2017, I spent some time looking into and improving the\u2026","rel":"","context":"In &quot;Expressions&quot;","block_context":{"text":"Expressions","link":"https:\/\/www.opengis.ch\/it\/category\/programming\/expressions\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":2021,"url":"https:\/\/www.opengis.ch\/it\/2016\/02\/04\/increasing-the-stability-of-processing-algorithms\/","url_meta":{"origin":2245,"position":1},"title":"Increasing the stability of processing algorithms","author":"Matthias Kuhn","date":"4 Febbraio 2016","format":false,"excerpt":"Processing just got a new testing framework to improve\u00a0the long-term stability of this important plugin. And you can help to improve it, even if you are not a software developer! This is yet another piece in our never-stopping crusade to improve the stability and quality of the best\u00a0desktop GIS on\u2026","rel":"","context":"In &quot;C++&quot;","block_context":{"text":"C++","link":"https:\/\/www.opengis.ch\/it\/category\/programming\/cpp\/"},"img":{"alt_text":"pr","src":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2016\/02\/pr.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2016\/02\/pr.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2016\/02\/pr.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2016\/02\/pr.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":1985,"url":"https:\/\/www.opengis.ch\/it\/2015\/12\/10\/geometry-generator-symbology\/","url_meta":{"origin":2245,"position":2},"title":"Geometry generator symbology","author":"Matthias Kuhn","date":"10 Dicembre 2015","format":false,"excerpt":"Say hello to geometry generators, a new way to use expression syntax to generate a geometry on the fly during the rendering process.","rel":"","context":"In &quot;Featured&quot;","block_context":{"text":"Featured","link":"https:\/\/www.opengis.ch\/it\/category\/featured\/"},"img":{"alt_text":"Configuration Dialog","src":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2015\/12\/Screenshot-from-2015-12-10-16-06-12-300x203.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":1867,"url":"https:\/\/www.opengis.ch\/it\/2015\/11\/02\/qgis-crowdfunding-2-5d-rendering\/","url_meta":{"origin":2245,"position":3},"title":"QGIS Crowdfunding: 2.5D Rendering","author":"Matthias Kuhn","date":"2 Novembre 2015","format":false,"excerpt":"","rel":"","context":"In &quot;3D&quot;","block_context":{"text":"3D","link":"https:\/\/www.opengis.ch\/it\/category\/3d\/"},"img":{"alt_text":"2.5D rendering","src":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2015\/10\/title.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2015\/10\/title.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2015\/10\/title.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2015\/10\/title.png?resize=700%2C400&ssl=1 2x"},"classes":[]},{"id":13328,"url":"https:\/\/www.opengis.ch\/it\/2022\/11\/15\/model-baker-interlis-data-validator\/","url_meta":{"origin":2245,"position":4},"title":"Model Baker INTERLIS Data Validator","author":"Dave Signer","date":"15 Novembre 2022","format":false,"excerpt":"The fully integrated Data Validator, which allows validating your data directly in QGIS against their INTERLIS model, exists for almost a year now. After lots of user feedback and some investments, it's now shinier than ever. Time for an update and a little step-by-step guide. Why is it so awesome?\u2026","rel":"","context":"In &quot;GIS&quot;","block_context":{"text":"GIS","link":"https:\/\/www.opengis.ch\/it\/category\/gis\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2022\/11\/8.png?fit=971%2C581&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2022\/11\/8.png?fit=971%2C581&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2022\/11\/8.png?fit=971%2C581&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2022\/11\/8.png?fit=971%2C581&ssl=1&resize=700%2C400 2x"},"classes":[]},{"id":3911,"url":"https:\/\/www.opengis.ch\/it\/2018\/05\/28\/how-to-filter-features-in-qgis-using-the-graphical-processing-modeler\/","url_meta":{"origin":2245,"position":5},"title":"How to filter features in QGIS using the graphical processing modeler","author":"Matthias Kuhn","date":"28 Maggio 2018","format":false,"excerpt":"This article describes a new algorithm for the processing modeler called feature filter algorithm. If you are already familiar with ETL concepts and the graphical modeler, you can directly jump to the section the feature filter algorithm. Building workflows for repetitive tasks When building workflows for simple or complex geodata\u2026","rel":"","context":"In &quot;Expressions&quot;","block_context":{"text":"Expressions","link":"https:\/\/www.opengis.ch\/it\/category\/programming\/expressions\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2018\/05\/modeller.png?fit=1016%2C497&ssl=1&resize=350%2C200","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2018\/05\/modeller.png?fit=1016%2C497&ssl=1&resize=350%2C200 1x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2018\/05\/modeller.png?fit=1016%2C497&ssl=1&resize=525%2C300 1.5x, https:\/\/i0.wp.com\/www.opengis.ch\/wp-content\/uploads\/2018\/05\/modeller.png?fit=1016%2C497&ssl=1&resize=700%2C400 2x"},"classes":[]}],"jetpack_shortlink":"https:\/\/wp.me\/pbdBtI-Ad","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/posts\/2245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/comments?post=2245"}],"version-history":[{"count":1,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/posts\/2245\/revisions"}],"predecessor-version":[{"id":11138,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/posts\/2245\/revisions\/11138"}],"wp:attachment":[{"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/media?parent=2245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/categories?post=2245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.opengis.ch\/it\/wp-json\/wp\/v2\/tags?post=2245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}