10

I have two oracle layer (point(p1) and polygon(pl1)). I want to build a rule-based symbology which is based on current selection on two cases:

Case 1- When I select a point from table p1, I want that the polygons from table pl1 to be coloured differently based one the relation that they have with table p1 which is p1.id_b=pl1.id_b (one to many).

Case 2- When I select a polygon from table pl1 other polygons from the same layer that are related to current selection to be coloured differently. Relation between polygons is on the field pl1.id_group, so polygons are grouped by the same pl1.id_group (one to many).

Software used is QGIS.

enter image description here

For illustration I select point one and the related polygons are coloured differently from the others.

  • 1
    What if two cases occur? For example, for three colored polygons based-on the selected point in the image, what if you select one of them while the point is already selected? – Kadir Şahbaz Oct 9 at 10:56
7

Solution for Case 1:

  • Add two symbol layers to the polygon layer as in the image. enter image description here

  • Open Based-on selected points rule, open expression window for Filter.

  • Go to Function Editor tab and add the following script as function.

    from qgis.core import *
    from qgis.gui import *
    
    @qgsfunction(args='auto', group='Custom')
    def related_rule_based(layer_name, field1, field2, feature, parent):
        layer = QgsProject.instance().mapLayersByName(layer_name)[0]
        selected_features = layer.selectedFeatures()
        if selected_features:
            feat = selected_features[0]
            if feat[field1] == feature[field2]:
                return True
            else:
                return False
    
  • Click Save and Load Functions. Then, in Expression tab, enter the next line: (p1: point layer name, first id_b: field in point layer, second id_b: field in polygon layer)

    related_rule_based('p1', 'id_b', 'id_b')
    

This solution is for just one selected point. If you select more points, one polygon group will remain selected.

enter image description here


Solution for Case 2:

  • Add one more rule to the polygon layer.

enter image description here

  • Enter the following expression as rule.

    related_rule_based(@layer_name, 'id_b', 'id_b')
    

Note: If two cases occur, one of both will be effective.

enter image description here

| improve this answer | |
  • What if i it has to go check in more then on field for matching value p1.id_b=pl1.id_b1 or p1.id_b=pl1.id_b2 or p1.id_b=pl1.id_b3.....p1.id_b=pl1.id_bn or vice versa pl1.id_b=p1.id_b1 or pl1.id_b=p1.id_b2 or pl1.id_b=p1.id_b3.....pl1.id_b=p1.id_bn – Shefqet LULJA Oct 10 at 8:17
  • You can add more parameters to function definition (field3, field4, ...) and more conditions to if statement. You can also create a separate function for case 2. – Kadir Şahbaz Oct 10 at 8:32
5

This script creates a rule based style which refreshes upon the selection of a feature with the logic you described.You only need to enter the layer names below and run it in QGIS.

class Restyler:
    def __init__(self, polygonLayerName, pointLayerName):
        self.polygonLayer = QgsProject.instance().mapLayersByName(polygonLayerName)[0]
        self.pointLayer = QgsProject.instance().mapLayersByName(pointLayerName)[0]

        self.polygonLayer.selectionChanged.connect(self.polygonSelected)
        self.pointLayer.selectionChanged.connect(self.pointSelected)
        self.addRules()

    def addRules(self):
        layer = self.polygonLayer
        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        renderer = QgsRuleBasedRenderer(symbol)
        root_rule = renderer.rootRule()
        rule = root_rule.children()[0].clone()
        rule.setFilterExpression('"id_b" = @selected_id_b')
        rule.symbol().setColor(QColor('pink'))

        rule2 = rule.clone()
        rule2.setFilterExpression('"id_group" = @selected_id_group')
        root_rule.appendChild(rule)
        root_rule.appendChild(rule2)
        layer.setRenderer(renderer)
        iface.layerTreeView().refreshLayerSymbology(layer.id())

    def pointSelected(self):
        if self.pointLayer.selectedFeatureCount() == 1:
            selectedIdb = self.pointLayer.selectedFeatures()[0]['id_b']
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_b', selectedIdb)
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_group', '')
            print("Point selected. id_b: {}".format(selectedIdb))
            self.polygonLayer.triggerRepaint()
        else:
            print("Error. One feature should be selected")
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_group', '')
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_b', '')

        self.polygonLayer.triggerRepaint()

    def polygonSelected(self):
        if self.polygonLayer.selectedFeatureCount() == 1:
            selectedIdGroup = self.polygonLayer.selectedFeatures()[0]['id_group']
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_group', selectedIdGroup)
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_b', '')
            print("Polygon selected. id_group: {}".format(selectedIdGroup))
        else:
            print("Error. One feature should be selected")
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_group', '')
            QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(),'selected_id_b', '')

        self.polygonLayer.triggerRepaint()

#you only need to input your layer names
polygonLayerName = 'wojewodztwa'
pointLayerName = 'testcsv_geocoded'

restyler = Restyler(polygonLayerName, pointLayerName)

result

| improve this answer | |
  • What if i it has to go check in more then on field for matching value p1.id_b=pl1.id_b1 or p1.id_b=pl1.id_b2 or p1.id_b=pl1.id_b3.....p1.id_b=pl1.id_bn or vice versa pl1.id_b=p1.id_b1 or pl1.id_b=p1.id_b2 or pl1.id_b=p1.id_b3.....pl1.id_b=p1.id_bn – Shefqet LULJA Oct 10 at 8:17
  • I wrote this as an example. If you need any other logic you can implement it. If you have trouble implementing anything specifically then feel free to ask. – Leon Powałka Oct 10 at 17:42

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.