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)
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"
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)
def test_MatchesTrueForFields(self): style = QgsConditionalStyle('"testfield" = @value') context = self.new_context() assert style.matches(20, context)
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)
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"
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)>")