Based on multiple posts like here and here, here's a way to automate rule-based symbology creation, making sure it handes all unique values from two property fields from a given Vector layer (Date and Origin in your case).
This works from the Python console on the active layer:
- It creates an empty Rule based renderer.
- Selects all Origins as possible categories in a CategoryRenderer
- Goes through all available Dates, adds a rule for data matching a given date and refines the rule with the above mentioned categories
I tested this code (with other field names) and it works. Some extra python code might be needed if your dates are not already in a YYYY-MM format. Also, for each date, all categories will be listed not only the ones that belond to that date range. This can be improved as well by reworking the script
from random import randrange
layer = iface.activeLayer()
symbol = QgsSymbol.defaultSymbol(layer.geometryType())
rb_renderer = QgsRuleBasedRenderer(symbol)
root_rule = rb_renderer.rootRule()
rule = root_rule.children()[0].clone()
dateindex = layer.dataProvider().fieldNameIndex("date_field")
unique_values_date = layer.uniqueValues(dateindex)
originindex = layer.dataProvider().fieldNameIndex("origin")
unique_values_origin = layer.uniqueValues(originindex)
categories = []
for unique_value in unique_values_origin:
# 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(unique_value, symbol, str(unique_value))
# entry for the list of category items
categories.append(category)
create renderer object
renderercat = QgsCategorizedSymbolRenderer('origin', categories)
print(renderercat)
dates = []
for unique_date in unique_values_date:
rule = root_rule.children()[0].clone()
rule.setLabel(str(unique_date))
rule.setFilterExpression(f""""date_field"='{unique_date}'""")
QgsRuleBasedRenderer.refineRuleCategories(rule,renderercat)
root_rule.appendChild(rule)
print(root_rule)
#QgsRuleBasedRenderer.refineRuleCategories(root_rule,renderercat)
print(rb_renderer)
assign the created renderer to the layer
if rb_renderer is not None:
layer.setRenderer(rb_renderer)
layer.triggerRepaint()
With this sample data:

here's what you get after running the script:
