If you are after new features and a ton of bug fixes, this release will have you smiling from ear to ear! QGIS 3.12 adds rich new capabilities to almost every part of QGIS. From label masks to a native PG Raster provider to incredible new mesh layer capabilities, and much, much more, this release has something for everyone. As always, we would like to remind you that QGIS is an open-source project and if you are able to, consider supporting our work through donations, sponsorship or contributions to the code documentation, web site and so on.

Thanks

We would like to thank the developers, documenters, testers and all the many folks out there who volunteer their time and effort (or fund people to do so). From the QGIS community, we hope you enjoy this release! If you wish to donate time, money or otherwise get involved in making QGIS more awesome, please wander along to qgis.org and lend a hand!

QGIS is supported by donors and sponsors. A current list of donors who have made financial contributions large and small to the project can be seen on our donors list. If you would like to become an official project sponsor, please visit our sponsorship page for details. Sponsoring QGIS helps us to fund our six monthly developer meetings, maintain project infrastructure and fund bug fixing efforts. A complete list of current sponsors is provided below - our very great thank you to all of our sponsors!

QGIS is Free software and you are under no obligation to pay anything to use it - in fact, we want to encourage people far and wide to use it regardless of what your financial or social status is - we believe empowering people with spatial decision-making tools will result in a better society for all of humanity.

The Settings - Options - Map Tools tab contains a new setting for controlling the default format to use for displaying angular bearings for newly created projects. Whenever a new project is created, it will inherit this default setting.

The Project Properties dialog also has a new setting for the project-specific bearing format.

The intention is that whenever angular bearings are shown in QGIS, they will be formatted using the current project’s bearing format settings.

Also includes lots of nice API additions providing a stable, easy to discover place for setting and retrieving settings like the bearing format.

It’s possible to copy features from one layer to another. If there are the same fields in the destination layer, then the attributes for them are taken from the original feature. If not, the default value is taken. Otherwise, the new attribute is null.

If the destination layer has constraints on the fields, these should be fulfilled now or disregarded on purpose. But not just copied invalid like it used to do.

That’s why now the attributes are checked against the constraints. And for all the invalid features a dialog pops up.

A new advanced rendering feature has been added: selective masking. It allows the definition of ‘masks’ areas around labels or point markers. These masks will “un-draw” only some symbol layers of other layers underneath, chosen by the user.

The legend finally also works nicely with data defined rotation. QGIS could already apply data defined rotation to symbols and individual symbol layers, but this resulted in a broken legend. The rotation of symbols (and symbol parts defined on symbol layers) on the legend can now be controlled by changing the default values next to the data defined properties.

The old behavior was to always fall back to 0 in case of a failing expression. Which made it hard to create a proper legend in some cases.

e.g. if you had two symbol layers with the following expressions for their rotation

"orientation"

And

90+"orientation"

which would be two orthogonal symbol layers (e.g. denote a right angle), rotated by an orientation field.

Now in the legend (and symbol preview), both layers would be shown with a rotation of 0, because there is no associated feature with a field orientation.

The new behavior is to fall back to the static, configured values which makes it very intuitive to configure the legend for these cases.

With this new feature users will have possibility to create stacks bars of varying colors for each attribute on top of each other vertically or horizontally. Designed to match the “Stacked” chart renderer option available in ArcGIS.

For datasets defined on faces, one can choose to interpolate data to vertices with neighbour average method. When no data interpolation method is chosen, each pixel on a single face has a single value/color. With data on vertices, the rendering for each pixel is interpolated from the values on the vertices, making smoother figures.

Use mesh contours styling panel to switch between the data interpolation methods.

For various dataset types, for example GRIB and NetCDF, the reference time in QGIS time settings dialog is prepopulated from the raw data and does not need to be set manually. Also we fixed various bugs related to time parsing, so in QGIS 3.12 it should be possible to format and show your time in plots/animations in the proper way:

If there is a valid time reference provided with dataset groups, this time reference is used to displaying time (using absolute time).

If there is no reference time provided, the time is displayed using relative time, and a time reference can be set by the user to display the absolute time.

When no time reference is provided, the default one is the current date + time set with 00:00:00.

A push-button is added to reload the reference time provided with dataset groups if needed.

It also adds a new feature to let the user set the time unit of the provider with a combo box if this time unit is different than hours.

The user can choose those enable/disable smooth triangles and wireframe. It is possible to choose the line width and the line color of the wireframe, change the vertical scale and choose the style of the rendering (unique color or color ramp shading).

There are two entries to render the mesh layer in the 3D view :

choose the mesh as the terrain in the config widget

activate the 3D view in the layer properties.

The user can choose those settings:

enable/disable smooth triangles

enable/disable wireframe

choose the line width and the line color of the wireframe

change the verticale scale

choose the style of the rendering : unique color or color ramp shading

settings the color ramp shading as for raster layer

For now, the mesh can be rendered as a TIN but rendering dataset is not supported. However, the infrastructure is adapted for a future round of work.

This adds support for background loading of data from vector layers into 3D map views. Until now, loading (and tessellation) would freeze the GUI completely - this could take many seconds depending on the complexity of input data.

The basic vector layer renderer and rule-based renderer were converted to use QgsChunkedEntity which is already used for terrain rendering. There are two more improvements in addition to unlocking of GUI:

loading process is multi-threaded instead of using just a single core

loading is done in tiles - so it is possible to see the tiles with 3D data appearing while other data are still being loaded

There is a new configuration option in the 3D tab of vector layers - it determines how deep the quadtree will be. For example, one zoom level means there will be a single tile for the whole layer. Three zoom levels means there will be 16 tiles at the leaf level (every extra zoom level multiplies that by 4, so I have limited GUI to max. 8 levels which gives ~16K tiles which is already a lot).

How a vector layer’s tiling quadtree gets populated: all internal tree nodes are empty and thus the 3D map scene tries to immediately replace them with their children - this goes until leaf nodes are reached. Only nodes at the leaf level currently hold any data. This may change in the future when we introduce more elaborate strategies - for example, internal nodes may contain a small percentage of features of the child nodes (this would allow us to show something while zoomed out a lot, not requiring to load all data).

For debugging purposes, there is also a new configuration option “show bounding boxes”. This allows you to see the bounding box of each tile (especially useful if there are some issues with tiles not showing up when they should).

This new item type allows for the creation of tables with contents manually entered by users (i.e. spreadsheet-style) so that users can create completely custom tables.
Supports control custom cell contents, foreground and background colors (and soon, preset row and column heights). A brand new table designer dialog has been added which allows for customization of these tables.

You now have the option for controlling the numeric format used by a layout scalebar.

You can manage all the formatting properties for the numbers in scale bars, including whether they want a thousand separator, decimal places, scientific notation, etc. Very useful in the case of making maps for audiences outside of the current QGIS locale, or when you’d just prefer to vary the style from the locale defaults (e.g. adding thousands separators when the locale default is to hide them).

When the new “Apply layer conditional styling colors” option is enabled in the layout attribute table settings, any conditional styling rules present in the layer will be applied inside the layout attribute table (foreground and background colors only, for now!).

When a field is a RelationReference, ValueRelation and ValueMap, there is the possibility not only to show the values of the current layer but also the possible values in the referenced layer / configured possibilities.

Takes an input layer, existing field and a new name for the field, and outputs a new layer with the selected field renamed.

While this result could also be achieved with the Refactor Fields algorithm, Refactor Fields isn’t particularly model friendly. It relies on a constant, fixed table structure, and can’t adapt to input tables with different field structures.

In contrast, this simple Rename Field algorithm adapts nicely for model use, because it operates on a single field only and leaves all the other fields untouched.

When a field is a RelationReference, ValueRelation and ValueMap, there is the possibility not only to show the values of the current layer but also the possible values in the referenced layer / configured possibilities.

age is a ValueMap, species is a ValueRelation and island_id is a RelationReference

In the example we have the persons:

George (in Cuba, in his twenties, human)
Paul (in Vietnam, in his thirties, human)
Ringo (in Venezuela, in his forties, cat)
John (in Vietnam as well, in his forties, table)

And the entries in the country layer are USSR, Cuba, Vietnam, Burma, Venezuela, North Korea

geom_from_wkb(geom_to_wkb(make_point(4,5)))→apointgeometryobject
Returns a geometry created from a Well-Known Binary (WKB) representation.

geom_to_wkb($geometry)→binaryblobcontainingageometryobject
Returns the Well-Known Binary (WKB) representation of a geometry as a binary blob.

Adds geom_from_wkb and geom_to_wkb, which mirror the existing
geom_from_wkt/geom_to_wkt functions but for WKB representations
of geometries.

Since QGIS 3.6 we’ve had good support for binary blob values in
expressions and field values, so adding these functions allows
users to work with binary blob fields containing WKB representations
of geometries (e.g. with a geometry generator showing the encoded
geometries)

datetime_from_epoch(1483225200000)→2017-01-01T00:00:00
Returns a datetime whose date and time are the number of milliseconds, msecs, that have passed since 1970-01-01T00:00:00.000, Coordinated Universal Time (Qt.UTC), and converted to Qt.LocalTime.

rand(10,80,1)→30
Returns a random integer within the range specified by the minimum and maximum argument (inclusive). If a seed is provided, the returned will always be the same, depending on the seed.

randf(10,80,1)→19.37136508087729
Returns a random float within the range specified by the minimum and maximum argument (inclusive). If a seed is provided, the returned will always be the same, depending on the seed.

This feature adds an optional seed parameter to rand() and randf() functions
This is very useful if you want the result to be deterministic, for instance to assign random but fixed colors to features. Using color_hsb(rand(0,360,$id),50,50) for instance yields always the same color for the same feature.
We also improves the rand() function, which didn’t work for high values (over 32000) by using Qt’s QRandomGenerator instead of qrand (which it seems was deprecated in Qt 5.11).

By default, those expression use the application’s locale. The addition of an optional language parameter allows handling of dates that wouldn’t match that default locale (say for e.g. an English system running QGIS trying to transform a French-formatted string into a date object).

On copy-paste features from one layer to another, in case there are constraints (e.g. not null) on the destination layer, that cannot bee fulfilled automatically by default values, a dialog pops up to fix the invalid attributes or disregard the constraints on purpose.

In previous version of QGIS, the snapping index cache was built sequentially and you had to wait for all your layers to be indexed before starting edition. Thanks to the QGIS.org grant program, QGIS now builds the snapping index cache in parallel for each layer, so it speeds up the whole process.
Snapping has also been relaxed, meaning that you don’t have to wait for the cache to be complete, you can start editing and snapping information will appear as soon as they are ready.

We did a revamp of the DXF export process. This solidifies the export process and offers new features.

Styles of geometries are exported and blocks are used

The Z coordinate of 3D geometries are preserved

Labels are exported with their anchor points and horizontal and vertical alignment or quadrant settings respected

The whole DXF export process has also been made ready for running in a thread.
With this in place, it’s now only one step away from being sent to the background, allow cancellation of an ongoing export process or being exposed as a processing algorithm.

We added the ability to add a new feature and digitize its geometry directly from within the relation editor widget. It’s now easier to add a geometric feature related to your currently displayed parent feature.

From the relation editor widget, you can link your currently displayed feature with existing features. The feature selection dialog allows you to choose these features. Thanks to the QWAT user group, feature selection is now shared with the canvas’ one so it is easy to find out and pick the feature you want to link.
We have also added the ability to filter displayed features (selected ones, visible on map, matching an expression…) reusing the same widgets already existing in attribute form.

A new algorithm in QGIS’s analysis library API to export directly contour lines and polygons is added. The method is not based on GDAL algorithms, but directly uses mesh layer triangular mesh interpolation methods. It is both fast and with smooth shapes, matching rendered images from QGIS. You can try the new processing algorithm in Crayfish processing toolbox.

You can use mesh calculator for all dataset types, both defined on faces and vertices. Additionally, it allows users to store the result of mesh calculator under different name or format. This allows for example to work with FLO-2D or HEC-RAS data in the QGIS mesh calculator

We improved the existing package layers processing algorithm to be able to add new layers to existing GeoPackages.
All you need to do to make use of this is disable the OVERWRITE parameter and specify an existing GeoPackage.

The Fuzzify raster (linear membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following a linear fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows a linear membership function.

The Fuzzify raster (power membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following a power fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows a power membership function.

The Fuzzify raster (small membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following the ‘small’ fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows the ‘small’ membership function. The ‘small’ function is constructed using two user-defined input raster values which set the point of half membership (midpoint, results to 0.5) and a predefined function spread which controls the function uptake.

The Fuzzify raster (large membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following the ‘large’ fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows the ‘large’ membership function.The ‘large’ function is constructed using two user-defined input raster values which set the point of half membership (midpoint, results to 0.5) and a predefined function spread which controls the function uptake.

The Fuzzify raster (gaussian membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following the ‘gaussian’ fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows the ‘gaussian’ membership function. The gaussian function is constructed using two user-defined input values which set the midpoint of the gaussian function (midpoint, results to 1) and a predefined function spread which controls the function spread.

The Fuzzify raster (near membership) algorithm is a native implementation of a fuzzy logic algorithm. It transforms an input raster to a fuzzified raster and thereby assigns values between 0 and 1 following the ‘near’ fuzzy membership function. The value of 0 implies no membership with the defined fuzzy set, a value of 1 depicts full membership. In between, the degree of membership of raster values follows the ‘near’ membership function. The near function is constructed using two user-defined input values which set the midpoint of the near function (midpoint, results to 1) and a predefined function spread which controls the function spread.

We ported the Densify by count algorithm to C++ in order to enhance it’s speed when compared to the previous Python implementation. The new algorithm also exposes the count parameter as dynamic parameter so that it can be controlled by expressions or field values.

We ported the Random points in extent algorithm to C++. This boosts it’s speed when comparing it to the previous Python implementation. The new algorithm also exposes an advanced parameter of maximum numbers of retrys for the algorithm when searching for randomly placed points that respect a certain distance between all points.

In this version we added a native algorithm to calculate the raster based density of lines. This algorithm calculates the line density based on a search radius and weights of the lines inside the search radius. The algorithm was ported to provide more functionality form the ArcGIS Spatial Analyst extension in QGIS.

This algorithm compares two vector layers, and determines which features are unchanged, added or deleted between the two. It is designed for comparing two different versions of the same dataset.

When comparing features, the original and revised feature geometries will be compared against each other. Depending on the Geometry Comparison Behavior setting,
the comparison will either be made using an exact comparison (where geometries must be an exact match for each other, including the order and count of vertices) or a
topological comparison only (where are geometries area considered equal if all of the their component edges overlap. E.g. lines with the same vertex locations but
opposite direction will be considered equal by this method). If the topological comparison is selected then any z or m values present in the geometries will not
be compared.

By default, the algorithm compares all attributes from the original and revised features. If the Attributes to Consider for Match parameter is changed, then only
the selected attributes will be compared (e.g. allowing users to ignore a timestamp or ID field which is expected to change between the revisions).

If any features in the original or revised layers do not have an associated geometry, then care must be taken to ensure that these features have a unique set of
attributes selected for comparison. If this condition is not met, warnings will be raised and the resultant outputs may be misleading.

The algorithm outputs three layers, one containing all features which are considered to be unchanged between the revisions, one containing features deleted from the original layer which are not present in the revised layer, and one containing features add to the revised layer which are not present in the original layer.

This allows for easy polygon->polygon joins, where you expect there to be only a single matching feature and don’t want to include features which are just touching or have just tiny sliver polygon overlaps.

Add customization of the items shown in browser to the Interface Customization dialog. User can hide some of the root items in the browser panel (e.g. Favourites, PostGIS provider, MSSQL, Oracle, Volumes, …)

MDAL and QGIS now supports 3D Stacked Meshes, particularly for TUFLOW-FV format. For this release, you need to choose appropriate averaging method in the QGIS interface and you are able to browse the data similarly to any other 2D dataset.

The situation was that we had two different code paths for handling GDAL side attribute decoding OR QGIS side decoding. Unfortunately, they are both incompatible with each other, and due to GDAL API for this, we can’t unify the two approaches. (More technical detail in the commit log message!)

So, now we:

always do the decoding on QGIS’ side. This allows users to manually override a shapefile’s declared encoding because they are often incorrect!

use a port of GDAL’s shapefile detection logic (it’s not exposed in GDAL API, so I had to re-implement it here) so that we default to reading shapefiles by respecting the embedded encoding information (via CPG files or DBF LDID information)

Completely remove the confusing/broken “Ignore shapefile encoding declaration” option, as it’s no longer required – users are ALWAYS able to manually change the encoding of shapefiles layers if needed

Always show users the detected embedded encoding in the layer properties, instead of always showing “UTF-8” when the embedded encoding information is used

This should give the best of both worlds – a nice default behavior resulting in shapefiles being read with the correct encoding, whilst still allowing users to override this on a layer-by-layer basis as needed.

This new provider is a client-side implementation of the recently adopted OGC API - Features - Part 1: Core specification, previously known as WFS3. It is integrated within the graphical user interface of the WFS provider, and leverages its core mechanisms to offer background downloading of features, using paging, and a local cache of already downloaded features for a smoother interactive use of datasets.

The actions to trigger the drawing tools were not exposed in the API, if you wanted to do an action for one of these tools, you had to recreate classes. So a call of the type
qgis.utils.iface.actionCircleCenterPoint().trigger() simplifies programmability.