def test_MatchesTrueForFields(self):
     feature = QgsFeature()
     fields = QgsFields()
     fields.append(QgsField("testfield", QVariant.Int))
     feature.setFields(fields, True)
     feature["testfield"] = 20
     style = QgsConditionalStyle('"testfield" = @value')
     assert style.matches(20, feature)
 def test_MatchesTrueForFields(self):
     feature = QgsFeature()
     fields = QgsFields()
     fields.append(QgsField("testfield", QVariant.Int))
     feature.setFields(fields, True)
     feature["testfield"] = 20
     style = QgsConditionalStyle('"testfield" = @value')
     context = QgsExpressionContextUtils.createFeatureBasedContext(feature, fields)
     assert style.matches(20, context)
Example #3
0
 def test_MatchingStylesReturnsListOfCorrectStyles(self):
     styles = []
     style = QgsConditionalStyle("@value > 10")
     style.setName("1")
     styles.append(style)
     style = QgsConditionalStyle("@value > 10")
     style.setName("2")
     styles.append(style)
     style = QgsConditionalStyle("@value < 5")
     style.setName("3")
     styles.append(style)
     context = self.new_context()
     out = QgsConditionalStyle.matchingConditionalStyles(styles, 20, context)
     assert len(out) == 2
     out[0].name() == "1"
     out[1].name() == "2"
Example #4
0
def hazard_class_style(layer, classification, display_null=False):
    """Set colors to the layer according to the hazard.

    :param layer: The layer to style.
    :type layer: QgsVectorLayer

    :param display_null: If we should display the null hazard zone. Default to
        False.
    :type display_null: bool

    :param classification: The hazard classification to use.
    :type classification: dict safe.definitions.hazard_classifications
    """
    categories = []

    # Conditional styling
    attribute_table_styles = []

    for hazard_class, (color, label) in list(classification.items()):
        if hazard_class == not_exposed_class['key'] and not display_null:
            # We don't want to display the null value (not exposed).
            # We skip it.
            continue

        symbol = QgsSymbol.defaultSymbol(layer.geometryType())
        symbol.setColor(color)
        if is_line_layer(layer):
            symbol.setWidth(line_width_exposure)
        category = QgsRendererCategory(hazard_class, symbol, label)
        categories.append(category)

        style = QgsConditionalStyle()
        style.setName(hazard_class)
        style.setRule("hazard_class='%s'" % hazard_class)
        style.setBackgroundColor(transparent)
        symbol = QgsSymbol.defaultSymbol(QgsWkbTypes.PointGeometry)
        symbol.setColor(color)
        symbol.setSize(3)
        style.setSymbol(symbol)
        attribute_table_styles.append(style)

    layer.conditionalStyles().setFieldStyles(
        'hazard_class', attribute_table_styles)
    renderer = QgsCategorizedSymbolRenderer(
        hazard_class_field['field_name'], categories)
    layer.setRenderer(renderer)
Example #5
0
 def test_MatchesTrueForFields(self):
     style = QgsConditionalStyle('"testfield" = @value')
     context = self.new_context()
     assert style.matches(20, context)
Example #6
0
 def test_MatchesReturnsTrueForComplexMatch(self):
     style = QgsConditionalStyle("@value > 10 and @value = 20")
     context = QgsExpressionContextUtils.createFeatureBasedContext(QgsFeature(), QgsFields())
     assert style.matches(20, context)
 def test_MatchesReturnsTrueForComplexMatch(self):
     style = QgsConditionalStyle("@value > 10 and @value = 20")
     assert style.matches(20)
 def test_MatchesReturnsTrueForSimpleMatch(self):
     style = QgsConditionalStyle("@value > 10")
     assert style.matches(20)
Example #9
0
 def test_MatchesReturnsTrueForComplexMatch(self):
     style = QgsConditionalStyle("@value > 10 and @value = 20")
     context = QgsExpressionContextUtils.createFeatureBasedContext(QgsFeature(), QgsFields())
     assert style.matches(20, context)
Example #10
0
 def test_MatchingStylesReturnsListOfCorrectStyles(self):
     styles = []
     style = QgsConditionalStyle("@value > 10")
     style.setName("1")
     styles.append(style)
     style = QgsConditionalStyle("@value > 10")
     style.setName("2")
     styles.append(style)
     style = QgsConditionalStyle("@value < 5")
     style.setName("3")
     styles.append(style)
     context = self.new_context()
     out = QgsConditionalStyle.matchingConditionalStyles(
         styles, 20, context)
     assert len(out) == 2
     out[0].name() == "1"
     out[1].name() == "2"
Example #11
0
 def test_MatchesTrueForFields(self):
     style = QgsConditionalStyle('"testfield" = @value')
     context = self.new_context()
     assert style.matches(20, context)
Example #12
0
class EnforceAttributeRulesAlgorithm(QgsProcessingAlgorithm):
    """
    Algorithm for applying user-defined attribute rules to
    verify the filling of database attributes.
    """

    RULES_SET = "RULES_SET"
    SELECTED = "SELECTED"
    POINT_FLAGS = "POINT_FLAGS"
    LINE_FLAGS = "LINE_FLAGS"
    POLYGON_FLAGS = "POLYGON_FLAGS"

    def __init__(self):
        """
        Constructor.
        """
        super().__init__()
        self.valAlg = ValidationAlgorithm()
        self.flagFields = self.valAlg.getFlagFields()
        self.font = QFont()
        self.font.setBold(True)
        self.conditionalStyle = QgsConditionalStyle()

    def initAlgorithm(self, config):
        """
        Parameter setting.
        """
        attributeRulesSetter = ParameterAttributeRulesSet(
            self.RULES_SET,
            description=self.tr("Attribute Rules Set")
        )
        attributeRulesSetter.setMetadata({
            "widget_wrapper": "DsgTools.gui.ProcessingUI.enforceAttributeRulesWrapper.EnforceAttributeRulesWrapper"
        })
        self.addParameter(attributeRulesSetter)

        self.addParameter(
            QgsProcessingParameterBoolean(
                self.SELECTED,
                self.tr("Process only selected features")
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.POINT_FLAGS,
                self.tr("Point flags")
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.LINE_FLAGS,
                self.tr("Linestring flags")
            )
        )
        self.addParameter(
            QgsProcessingParameterFeatureSink(
                self.POLYGON_FLAGS,
                self.tr("Polygon flags")
            )
        )

    def parameterAsAttributeRulesSet(self, parameters, name, context):
        """
        Adds data from wrapper to algorithm parameters.
        :param parameters: (QgsProcessingParameter) a set of algorithm
            parameters;
        :param name: (json) JSON formatted attribute rules;
        :param context: (QgsProcessingContext) context in which
            processing was run;
        :return: (dict) parameters dictionary.
        """
        return parameters[name]

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        :param parameters: (QgsProcessingParameter) a set of algorithm
            parameters.
        :param context: (QgsProcessingContext) context in which
            processing was run.
        :param feedback: (QgsProcessingFeedback) QGIS progress tracking
                         component.
        :return: (dict) filled flag layers.
        """

        rules = self.parameterAsAttributeRulesSet(
            parameters, self.RULES_SET, context
        )

        if not rules:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.RULES_SET)
            )

        onlySelected = self.parameterAsBool(
            parameters, self.SELECTED, context)

        crs = QgsProject.instance().crs()
        pointFlags, ptId = self.parameterAsSink(
            parameters, self.POINT_FLAGS, context,
            self.flagFields, QgsWkbTypes.Point, crs)

        if not pointFlags:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.POINT_FLAGS))

        lineFlags, lId = self.parameterAsSink(
            parameters, self.LINE_FLAGS, context,
            self.flagFields, QgsWkbTypes.LineString, crs)

        if not lineFlags:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.LINE_FLAGS))

        polygonFlags, polId = self.parameterAsSink(
            parameters, self.POLYGON_FLAGS, context,
            self.flagFields, QgsWkbTypes.Polygon, crs)

        if not polygonFlags:
            raise QgsProcessingException(
                self.invalidSourceError(parameters, self.POLYGON_FLAGS))

        failedFeatures = self.applyAttrRules(rules, onlySelected)

        self.flagsFromFailedList(failedFeatures,
                                    pointFlags,
                                    lineFlags,
                                    polygonFlags,
                                    feedback)
        return {
            self.POINT_FLAGS: ptId,
            self.LINE_FLAGS: lId,
            self.POLYGON_FLAGS: polId}

    def applyAttrRules(self, attrRulesMap, onlySelected):
        """
        Filters a layer or a set of selected features as from conditional rules,
        and the result is added to a list in a dictionary.
        :param attrRulesMap: (dict) dictionary with conditional rules;
        :param onlySelected: (boolean) indicates whether the attribute rules
            should be applied exclusively on selected features of each
            verified layer;
        :return: (dict) modified attrRulesMap with filtered features.
        """
        proj = QgsProject.instance()

        for ruleOrder, ruleParam in attrRulesMap.items():
            if onlySelected:
                lyr = proj.mapLayersByName(ruleParam["layerField"][0])[0]
                request = QgsFeatureRequest().setFilterExpression(
                    ruleParam["expression"])
                selectedFeatures = lyr.getSelectedFeatures(request)
                ruleParam["features"] = [
                    feature for feature in selectedFeatures]
            else:
                lyr = proj.mapLayersByName(ruleParam["layerField"][0])[0]
                ruleParam["features"] = [
                    feature for feature in lyr.getFeatures(ruleParam["expression"])]
            self.applyConditionalStyle(proj.mapLayersByName(
                ruleParam["layerField"][0])[0], ruleParam)
        return attrRulesMap

    def flagsFromFailedList(self, attrRulesMap, ptLayer, lLayer, polLayer, feedback):
        """
        Creates new features from a failed conditional rules dictionary.
        :param attrRulesMap: (dict) dictionary with conditional rules;
        :param ptLayer: (QgsVectorLayer) output point vector layer;
        :param lLayer: (QgsVectorLayer) output line vector layer;
        :param polLayer: (QgsVectorLayer) output polygon vector layer;
        :param feedback: (QgsProcessingFeedback) QGIS progress tracking
                         component;
        :return: (tuple-of-QgsVectorLayer) filled flag layers.
        """
        layerMap = {
            QgsWkbTypes.PointGeometry: ptLayer,
            QgsWkbTypes.LineGeometry: lLayer,
            QgsWkbTypes.PolygonGeometry: polLayer
        }

        for ruleParam in attrRulesMap.values():
            flagText = "{name}".format(name=ruleParam["description"])
            for flag in ruleParam["features"]:
                geom = flag.geometry()
                newFeature = QgsFeature(self.flagFields)
                newFeature["reason"] = flagText
                newFeature.setGeometry(geom)
                layerMap[geom.type()].addFeature(
                    newFeature, QgsFeatureSink.FastInsert
                )
        self.logResult(attrRulesMap, feedback)
        return (ptLayer, lLayer, polLayer)

    def applyConditionalStyle(self, lyr, values):
        """
        Applies a conditional style for each wrong attribute
        in the attribute table.
        :param lyr: (QgsVectorLayer) vector layer;
        :param values: (dict) dictionary with conditional rules.
        """
        self.conditionalStyle.setRule(values["expression"])
        self.conditionalStyle.setFont(self.font)
        self.conditionalStyle.setTextColor(QColor(255, 255, 255))

        for field in lyr.fields():
            if field.name() in values["layerField"]:
                if isinstance(values["color"], (list, tuple)):
                    self.conditionalStyle.setBackgroundColor(
                        QColor(
                            values["color"][0],
                            values["color"][1],
                            values["color"][2]))
                else:
                    self.conditionalStyle.setBackgroundColor(
                        QColor(values["color"]))

                lyr.conditionalStyles().setFieldStyles(
                    field.name(), [self.conditionalStyle])

    def logResult(self, attrRulesMap, feedback):
        """
        Creates a statistics text log from each layer and your
        respectively wrong attribute.
        :param attrRulesMap: (dict) dictionary with conditional rules;
        :param feedback: (QgsProcessingFeedback) QGIS progress tracking
                         component.
        """
        feedback.pushInfo("{0} {1} {0}\n".format(
            "===" * 5, self.tr("LOG START")))

        for ruleParam in attrRulesMap.values():
            if len(ruleParam["features"]) > 0:
                row = self.tr("[RULE]") + ": {0} - {1}\n{2}: {3} {4}\n".format(
                    ruleParam["layerField"][1],
                    ruleParam["errorType"],
                    ruleParam["layerField"][0],
                    len(ruleParam["features"]),
                    self.tr("features") if len(ruleParam["features"]) > 1 else self.tr("feature"))
                feedback.pushInfo(row)
            else:
                pass

        feedback.pushInfo("{0} {1} {0}\n".format(
            "===" * 5, self.tr("LOG END")))

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return "enforceattributerulesalgorithm"

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return self.tr("Enforce Attribute Rules")

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr("Quality Assurance Tools (Identification Processes)")

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return "DSGTools: Quality Assurance Tools (Identification Processes)"

    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate("EnforceAttributeRulesAlgorithm", string)

    def createInstance(self):
        """
        Must return a new copy of your algorithm.
        """
        return EnforceAttributeRulesAlgorithm()
 def testQgsConditionalStyle(self):
     b = QgsConditionalStyle('@value > 20')
     self.assertEqual(b.__repr__(), "<QgsConditionalStyle: @value > 20>")
     b.setName('test name')
     self.assertEqual(b.__repr__(),
                      "<QgsConditionalStyle: 'test name' (@value > 20)>")