3

How can I query the symbology of a layer so that I have as many symbols as all possible combinations of "chrono1", "chrono2", "chrono3"? I'm looking for an automatic way to find all possible combinations of an array and turn them into symbols.

I could write the rules by hand but I would like to find an automatic way. Is it possible?

example

The combinations will be many more than those visible in the example image. Therefore I need to find an easier way to categorize the symbols.

7

You could use the Categorized renderer with this expression "chrono1"+"chrono2"+"chrono3" in the value field.

enter image description here

This will create all already existing combinations (so not all possible combinations) but if you edit your data and create new combination you could add them by either reclassifying (but that will change all symbology) or by clicking the "+" button and manually setting the new combination

7

In addition to @J.R's answer, you can adapt this answer from SO together with this answer from GIS SE to get a list of all possible combinations using Python/PyQGIS and set up a categorized renderer with all theoretically possible categories automagically.

Just copy paste the script to your Python editor in QGIS and change the name-strings in the fields-list (5th line) to your needs. Then select your layer and hit run. Depending on your number of features, number of fields and possible combinations it can take a while.

### Imports and Settings
import itertools as it
from random import randrange

layer = iface.activeLayer() # the layer you want to work with
fields = ['chrono_1','chrono_2','chrono_3','chrono_4'] # the fieldnames you want to consider

### This is the part to generate a list of all possible combinations
fieldsValuesDict = {}
possibleCombinations = []

# create a dictionary with fieldnames as keys
for field in fields:
    fieldsValuesDict[field] = []

# fill the dictionary with lists of all values in the layer
for feat in layer.getFeatures():
    for field in fields:
        if feat[field] not in (None,NULL):
            fieldsValuesDict[field].append(feat[field])
        else:
            fieldsValuesDict[field].append('')

# remove duplicate values from the dictionaries values-list
for k, v in fieldsValuesDict.items():
    fieldsValuesDict[k] = list(dict.fromkeys(v))

#print(fieldsValuesDict)
# create a list of all possible combinations of all value-lists of all keys in the dictionary
allNames = sorted(fieldsValuesDict)
combinations = it.product(*(fieldsValuesDict[Name] for Name in allNames))
for item in list(combinations):
    possibleCombinations.append(''.join(item))

possibleCombinations.sort()
#print(possibleCombinations)

### This is the part to setup the categorized renderer to all possible combinations
categories = []
for combination in possibleCombinations:
    # initialize the default symbol for this geometry type
    symbol = QgsSymbol.defaultSymbol(layer.geometryType())

    # configure a symbol layer
    layer_style = {}
    layer_style['color'] = '%d, %d, %d' % (randrange(0, 256), randrange(0, 256), randrange(0, 256))
    layer_style['outline'] = '#000000'
    symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

    # replace default symbol layer with the configured one
    if symbol_layer is not None:
        symbol.changeSymbolLayer(0, symbol_layer)

    # create renderer object
    category = QgsRendererCategory(combination, symbol, str(combination))
    # entry for the list of category items
    categories.append(category)

# create renderer object
mycat = '+'.join(fields)
renderer = QgsCategorizedSymbolRenderer(mycat, categories)

# assign the created renderer to the layer
if renderer is not None:
    layer.setRenderer(renderer)

# refresh
layer.triggerRepaint()

Demo:

enter image description here

9
  • 1
    @MrXsqared this is exactly what I need. However, when I run the code, the console gives me an error: exec(open('/var/folders/s5/k0t_jqpx3dddt02r2n_llc9w0000gn/T/tmpnzbej5vb.py'.encode('utf-8')).read()) Traceback (most recent call last): File "/Applications/QGIS.app/Contents/MacOS/../Resources/python/code.py", line 90, in runcode exec(code, self.locals) File "<input>", line 1, in <module> File "<string>", line 23, in <module> TypeError: sequence item 0: expected str instance, QVariant found
    – ddomizia
    2 days ago
  • 1
    @MrXsquared I can see your progress :)
    – Taras
    2 days ago
  • 1
    @ddomizia thanks for the hint, just fixed that. The error occured if a field is None or NULL. Just added if feat[field] not in (None,NULL): at line 19 to prevent the tuples from containing these values. @Taras thanks :)
    – MrXsquared
    2 days ago
  • 1
    Thanks @MrXsquared! Now the error is no longer there. However I only have one combination as a result
    – ddomizia
    2 days ago
  • 1
    @MrXsquared yes, the only difference is that I also have pairs of letters: for example: chrono_1 "A"; chrono_2 "PA"; chrono_3 "FA"; chrono_4 "I". Can this be the problem? I have tried closing and reopening QGIS but the situation does not change.
    – ddomizia
    2 days ago

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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