6

I'm using categorized style to show landownership on a parcel layer on QGIS (parcel layer is polygons). Is there a way to vary by fill pattern rather than color other than changing each one individually?

2 Answers 2

5

It's not as easy as using a color ramp, but you can build it using expressions and symbol layers.

QGIS doesn't have a standard palette of fill patterns that you could rotate through based on an index. But you can create multiple symbol layers that get turned on and off, and whose parameters change via data-defined overrides.

The following is an example that will do it as a reusable symbol you can save in your style library. It uses hatching that is diagonal Left, Right, Horizontal, Vertical, Square (=H and V combined), or X (=L and R combined).

As written, it conditions based on a field SymbolClass being one of those 6 letters. You can change it to refer to your categorized field directly, or -- for reusability -- keep it as is, but create a (possibly virtual) field SymbolClass in your layer that contains one of those 6 letters for every feature.

You'll use single symbol renderer, with extra logic, rather than categorized renderer. That provides a lot more flexibility, but impacts your legend.

As a model, choose single symbol renderer and from the visual style gallery, choose the hashed black / symbol or equivalent in a different colour.

Now in the symbol layer tree, choose the Line pattern fill line. Rotation will be 45 degrees. Create a data-defined override (icon to the right beside the field) with expression

map_get(map('L',45,'R',-45,'H',0,'V',90,'X',45,'S',0), "SymbolClass" )

This sets the rotation angle according to the SymbolClass. Right now, X and S match L and H. We need to add the 2nd hatches for them. To do so, in the symbol tree duplicate the Line pattern fill layer. In the 2nd copy, change the rotation data-defined override expression to

map_get(map('X',-45,'S',90), "SymbolClass" )

This adds the required cross-hatches. For this 2nd layer, under Enable symbol layer, create a data-defined override with expression "SymbolClass" in ('X','S') so the 2nd hatches appear only when you want them. (Otherwise, while the rotation expression would return NULL for the other categories, they would still be displayed, just with the default, non-over-ridden rotation of 45.)

This is what it looks like, each feature labelled by its SymbolClass:

Example hatches

You can save this compound symbol by going to the topmost layer in the symbol layer tree (Fill) and choosing Save symbol in bottom right to save it to your Style manager for re-use.

You could of course expand on this, e.g. by adding a 3rd point pattern fill symbol layer along side the 2 line pattern ones, that gets enabled only when SymbolClass is D and fills with dots instead. Etc, etc.

As described, this uses an attribute SymbolClass which gives the instructions how to render each feature, emulating but replacing the categorized renderer. For one-off use, you could change the expressions above to directly condition on your desired existing data field. Alternately, you can create SymbolClass a new attribute which you manually or via a processing algorithm fill in. You could even create it as a virtual field with logic specified by an expression, probably with some if or CASE WHEN statements, or another map, e.g.

map_get(map('me','X','evil neighbour','H','township','V'), "ParcelOwnership" )

Editing to add: I had forgotten that the Simple fill style allows you to use some predefined brushes. So rather than constructing layers of Line Pattern Fills and mucking with their rotation, you could just use a Simple fill with a data-driven override that's a field or expression that returns one of the precise strings listed in [ ] under Expected input in the picture below. However, I find these predefined brushes are not very pretty and can barely be customized, and so the approach above is preferable. YMMV.

Simple fill example

1
  • 1
    This works but isn't reflected in the table of contents. You can use is in combination with data-defined overrides for color to differentiate both by color and texture. I found you can cycle through a selection of the fill styles with map_get(map(0, 'b_diagonal', 1, 'horizontal', 2, 'vertical', 3, 'cross', 4, 'f_diagonal', 5, 'diagonal_x' ), $id % 6), and ended up using this method to draw a subtle white line over the top of categorized fill to help my color blind reviewer distinguish the differences Mar 11, 2021 at 0:18
3

You could create your symbols and name them using some unique keyword, like "hashed" or "pattern" then try something like:

style=QgsStyle.defaultStyle() #https://gis.stackexchange.com/questions/284648/get-a-list-of-all-symbols-out-of-the-qgis-style-manger-via-pyqgis

keywords = ['hashed','pattern']
stylelist = []
for stylename in style.symbolNames():
    if any(substring in stylename for substring in keywords):
        stylelist.append(style.symbol(stylename))

#https://gis.stackexchange.com/questions/175068/applying-categorized-symbol-to-each-feature-using-pyqgis
lyr = QgsProject.instance().mapLayersByName('ok_ak_riks')[0]
fieldname = 'lan_kod'
categories = []
vals = lyr.dataProvider().uniqueValues(lyr.fields().indexFromName(fieldname))
for val, sym in zip(vals, stylelist):
    category = QgsRendererCategory(val, sym, str(val))
    # entry for the list of category items
    categories.append(category)
renderer = QgsCategorizedSymbolRenderer(fieldname, categories)
if renderer is not None:
    lyr.setRenderer(renderer)

lyr.triggerRepaint()

enter image description here

1
  • 1
    This works and is reflected in the legend. If you have more unique values than symbols, the rest will be painted blank. I think you should be able to define a set and cycle through them to handle that instance. Mar 11, 2021 at 0:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.