def test_create_nulls_and_defaults(self):
        """Test bug #21304 when pasting features from another layer and default values are not honored"""

        vl = createLayerWithOnePoint()
        vl.setDefaultValueDefinition(1, QgsDefaultValue('300'))

        features_data = []
        context = vl.createExpressionContext()
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 44)'), {0: 'test_1', 1: None}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 45)'), {0: 'test_2', 1: QVariant()}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 46)'), {0: 'test_3', 1: QVariant(QVariant.Int)}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 46)'), {0: 'test_4'}))
        features = QgsVectorLayerUtils.createFeatures(vl, features_data, context)

        for f in features:
            self.assertEqual(f.attributes()[1], 300, f.id())

        vl = createLayerWithOnePoint()
        vl.setDefaultValueDefinition(0, QgsDefaultValue("'my_default'"))

        features_data = []
        context = vl.createExpressionContext()
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 44)'), {0: None}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 45)'), {0: QVariant()}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 46)'), {0: QVariant(QVariant.String)}))
        features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 46)'), {}))
        features = QgsVectorLayerUtils.createFeatures(vl, features_data, context)

        for f in features:
            self.assertEqual(f.attributes()[0], 'my_default', f.id())
Example #2
0
    def test_make_features_compatible_geometry(self):
        """Test corner cases for geometries"""

        # Make a feature with no geometry
        layer = self._make_layer('Point')
        self.assertTrue(layer.isValid())
        self.assertTrue(layer.startEditing())
        f1 = QgsFeature(layer.fields())
        f1.setAttributes([1])

        # Check that it is accepted on a Point layer
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], layer)
        self.assertEqual(len(new_features), 1)
        self.assertEqual(new_features[0].geometry().asWkt(), '')

        # Make a geometry-less layer
        nogeom_layer = QgsMemoryProviderUtils.createMemoryLayer(
            'nogeom_layer', layer.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem(4326))
        # Check that a geometry-less feature is accepted
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], nogeom_layer)
        self.assertEqual(len(new_features), 1)
        self.assertEqual(new_features[0].geometry().asWkt(), '')

        # Make a geometry-less layer
        nogeom_layer = QgsMemoryProviderUtils.createMemoryLayer(
            'nogeom_layer', layer.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem(4326))
        # Check that a Point feature is accepted but geometry was dropped
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], nogeom_layer)
        self.assertEqual(len(new_features), 1)
        self.assertEqual(new_features[0].geometry().asWkt(), '')
    def testVectorLayerUtilsUniqueWithProviderDefault(self):
        vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres")
        default_clause = "nextval('qgis_test.\"someData_pk_seq\"'::regclass)"
        self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause)
        self.assertTrue(QgsVectorLayerUtils.valueExists(vl, 0, 4))

        vl.startEditing()
        f = QgsFeature(vl.fields())
        f.setAttribute(0, default_clause)
        self.assertTrue(vl.addFeatures([f]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(vl, 0, default_clause))
Example #4
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPoint(QgsPoint(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().exportToWkt(), g.exportToWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueExpression(2, '3*4')
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12.0])
        # we expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 12.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueExpression(2, '3*$x')
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueExpression(2, None)

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])
Example #5
0
    def testVectorLayerUtilsCreateFeatureWithProviderDefault(self):
        vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres")
        default_clause = 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)'
        self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause)

        # check that provider default clause takes precedence over passed attribute values
        # this also checks that the inbuilt unique constraint handling is bypassed in the case of a provider default clause
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
        self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", "'qgis'::text", None, None])

        # test take vector layer default value expression overrides postgres provider default clause
        vl.setDefaultValueDefinition(3, QgsDefaultValue("'mappy'"))
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
        self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'mappy', None, None])
Example #6
0
    def testCreateUniqueValue(self):
        """ test creating a unique value """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # bad field indices
        self.assertFalse(QgsVectorLayerUtils.createUniqueValue(layer, -10))
        self.assertFalse(QgsVectorLayerUtils.createUniqueValue(layer, 10))

        # integer field
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 1), 128)

        # double field
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 2), 3.0)

        # string field
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 0), 'test_4')
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 0, 'test_1'), 'test_4')
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 0, 'seed'), 'seed')
        self.assertEqual(QgsVectorLayerUtils.createUniqueValue(layer, 0, 'superpig'), 'superpig_1')
    def testVectorLayerUtilsCreateFeatureWithProviderDefaultLiteral(self):
        vl = QgsVectorLayer("dbname=%s table='test_defaults' key='id'" % self.dbname, "test_defaults", "spatialite")
        self.assertEqual(vl.dataProvider().defaultValue(2), 5)

        f = QgsVectorLayerUtils.createFeature(vl)
        self.assertEqual(f.attributes(), [None, "qgis 'is good", 5, 5.7, None])

        # check that provider default literals take precedence over passed attribute values
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 'qgis is great', 0: 3})
        self.assertEqual(f.attributes(), [3, "qgis 'is good", 5, 5.7, None])

        # test that vector layer default value expression overrides provider default literal
        vl.setDefaultValueDefinition(3, QgsDefaultValue("4*3"))
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 'qgis is great', 0: 3})
        self.assertEqual(f.attributes(), [3, "qgis 'is good", 5, 12, None])
Example #8
0
    def testVectorLayerUtilsUniqueWithProviderDefault(self):
        vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres")
        default_clause = 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)'
        vl.dataProvider().setProviderProperty(QgsDataProvider.EvaluateDefaultValues, False)
        self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause)
        self.assertTrue(QgsVectorLayerUtils.valueExists(vl, 0, 4))

        vl.startEditing()
        f = QgsFeature(vl.fields())
        f.setAttribute(0, default_clause)
        self.assertFalse(QgsVectorLayerUtils.valueExists(vl, 0, default_clause))
        self.assertTrue(vl.addFeatures([f]))

        # the default value clause should exist...
        self.assertTrue(QgsVectorLayerUtils.valueExists(vl, 0, default_clause))
        # but it should not prevent the attribute being validated
        self.assertTrue(QgsVectorLayerUtils.validateAttribute(vl, f, 0))
        vl.rollBack()
    def test_make_features_compatible_different_field_length(self):
        """Test regression #21497"""
        fields = QgsFields()
        fields.append(QgsField('int_f1', QVariant.Int))
        f1 = QgsFeature(fields)
        f1.setAttributes([12345])
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))

        fields = QgsFields()
        fields.append(QgsField('int_f2', QVariant.Int))
        fields.append(QgsField('int_f1', QVariant.Int))
        vl2 = QgsMemoryProviderUtils.createMemoryLayer(
            'mymultiplayer', fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], vl2)
        self.assertEqual(new_features[0].attributes(), [None, 12345])

        f1.setGeometry(QgsGeometry.fromWkt('MultiPoint((9 45))'))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], vl2)
        self.assertEqual(new_features[0].attributes(), [None, 12345])
Example #10
0
    def test_make_features_compatible_attributes(self):
        """Test corner cases for attributes"""

        # Test feature without attributes
        fields = QgsFields()
        fields.append(QgsField('int_f', QVariant.Int))
        fields.append(QgsField('str_f', QVariant.String))
        layer = QgsMemoryProviderUtils.createMemoryLayer(
            'mkfca_layer', fields, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326))
        self.assertTrue(layer.isValid())
        f1 = QgsFeature(layer.fields())
        f1['int_f'] = 1
        f1['str_f'] = 'str'
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], layer)
        self.assertEqual(new_features[0].attributes(), f1.attributes())
        self.assertTrue(new_features[0].geometry().asWkt(), f1.geometry().asWkt())

        # Test pad with 0 with fields
        f1.setAttributes([])
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], layer)
        self.assertEqual(len(new_features[0].attributes()), 2)
        self.assertEqual(new_features[0].attributes()[0], QVariant())
        self.assertEqual(new_features[0].attributes()[1], QVariant())

        # Test pad with 0 without fields
        f1 = QgsFeature()
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], layer)
        self.assertEqual(len(new_features[0].attributes()), 2)
        self.assertEqual(new_features[0].attributes()[0], QVariant())
        self.assertEqual(new_features[0].attributes()[1], QVariant())

        # Test drop extra attrs
        f1 = QgsFeature(layer.fields())
        f1.setAttributes([1, 'foo', 'extra'])
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f1], layer)
        self.assertEqual(len(new_features[0].attributes()), 2)
        self.assertEqual(new_features[0].attributes()[0], 1)
        self.assertEqual(new_features[0].attributes()[1], 'foo')
    def test_create_multiple_unique_constraint(self):
        """Test create multiple features with unique constraint"""

        vl = createLayerWithOnePoint()
        vl.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)

        features_data = []
        context = vl.createExpressionContext()
        for i in range(2):
            features_data.append(QgsVectorLayerUtils.QgsFeatureData(QgsGeometry.fromWkt('Point (7 44)'), {0: 'test_%s' % i, 1: 123}))
        features = QgsVectorLayerUtils.createFeatures(vl, features_data, context)

        self.assertEqual(features[0].attributes()[1], 124)
        self.assertEqual(features[1].attributes()[1], 125)
Example #12
0
    def _make_compatible_tester(self, feature_wkt, layer_wkb_name, attrs=[1]):
        layer = self._make_layer(layer_wkb_name)
        layer.startEditing()

        f = QgsFeature(layer.fields())
        f.setAttributes(attrs)
        f.setGeometry(QgsGeometry.fromWkt(feature_wkt))
        self.assertTrue(f.isValid())

        context = QgsProcessingContext()
        context.setProject(QgsProject.instance())

        # Fix it!
        new_features = QgsVectorLayerUtils.makeFeaturesCompatible([f], layer)

        for new_f in new_features:
            self.assertEqual(new_f.geometry().wkbType(), layer.wkbType())

        self.assertTrue(layer.addFeatures(new_features), "Fail: %s - %s - %s" % (feature_wkt, attrs, layer_wkb_name))
        return layer, new_features
Example #13
0
    def check_overlaps_in_boundaries(self, db, layer_dict):
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Line.OVERLAPS_IN_BOUNDARIES)
        boundary_layer = list(layer_dict[QUALITY_RULE_LAYERS].values()
                              )[0] if layer_dict[QUALITY_RULE_LAYERS] else None

        if not boundary_layer:
            return QCoreApplication.translate(
                "LineQualityRules",
                "'Boundary' layer not found!"), Qgis.Critical

        # Create error layers structure
        error_point_layer = QgsVectorLayer(
            "MultiPoint?crs={}".format(boundary_layer.sourceCrs().authid()),
            "{} (puntos)".format(rule.error_table_name), "memory")
        data_provider = error_point_layer.dataProvider()
        data_provider.addAttributes(rule.error_table_fields)
        error_point_layer.updateFields()

        error_line_layer = QgsVectorLayer(
            "MultiLineString?crs={}".format(
                boundary_layer.sourceCrs().authid()),
            "{} (líneas)".format(rule.error_table_name), "memory")
        data_provider = error_line_layer.dataProvider()
        data_provider.addAttributes(rule.error_table_fields)
        error_line_layer.updateFields()

        if boundary_layer:
            overlapping = self.geometry.get_overlapping_lines(boundary_layer)
            if overlapping is None:
                return (QCoreApplication.translate(
                    "LineQualityRules",
                    "There are no boundaries to check for overlaps!"),
                        Qgis.Warning)

            else:
                points_intersected = overlapping[
                    'ladm_col:copy_vector_layer_1:Intersected_Points']
                lines_intersected = overlapping[
                    'native:extractbyexpression_3:Intersected_Lines']

                if isinstance(points_intersected, QgsVectorLayer):
                    point_features = list()
                    if points_intersected.featureCount() > 0:
                        for feature in points_intersected.getFeatures():
                            new_feature = QgsVectorLayerUtils().createFeature(
                                error_point_layer, feature.geometry(), {
                                    0:
                                    feature[db.names.T_ILI_TID_F],
                                    1:
                                    feature["{}_2".format(
                                        db.names.T_ILI_TID_F)],
                                    2:
                                    self.quality_rules_manager.
                                    get_error_message(
                                        QUALITY_RULE_ERROR_CODE_E200101),
                                    3:
                                    QUALITY_RULE_ERROR_CODE_E200101
                                })
                            point_features.append(new_feature)
                    error_point_layer.dataProvider().addFeatures(
                        point_features)

                if isinstance(lines_intersected, QgsVectorLayer):
                    line_features = list()
                    if lines_intersected.featureCount() > 0:
                        for feature in lines_intersected.getFeatures():
                            new_feature = QgsVectorLayerUtils().createFeature(
                                error_line_layer, feature.geometry(), {
                                    0:
                                    feature[db.names.T_ILI_TID_F],
                                    1:
                                    feature["{}_2".format(
                                        db.names.T_ILI_TID_F)],
                                    2:
                                    self.quality_rules_manager.
                                    get_error_message(
                                        QUALITY_RULE_ERROR_CODE_E200101),
                                    3:
                                    QUALITY_RULE_ERROR_CODE_E200101
                                })
                            line_features.append(new_feature)

                    error_line_layer.dataProvider().addFeatures(line_features)

                if error_point_layer.featureCount(
                ) == 0 and error_line_layer.featureCount() == 0:
                    return QCoreApplication.translate(
                        "LineQualityRules",
                        "There are no overlapping boundaries."), Qgis.Success
                else:
                    msg = ""
                    if error_point_layer.featureCount(
                    ) and error_line_layer.featureCount():
                        self.app.gui.add_error_layer(db, error_point_layer)
                        self.app.gui.add_error_layer(db, error_line_layer)
                        msg = QCoreApplication.translate(
                            "LineQualityRules",
                            "Two memory layers with overlapping boundaries ({} point intersections and {} line intersections) have been added to the map."
                        ).format(error_point_layer.featureCount(),
                                 error_line_layer.featureCount())
                    elif error_point_layer.featureCount():
                        self.app.gui.add_error_layer(db, error_point_layer)
                        msg = QCoreApplication.translate(
                            "LineQualityRules",
                            "A memory layer with {} overlapping boundaries (point intersections) has been added to the map."
                        ).format(error_point_layer.featureCount())
                    elif error_line_layer.featureCount():
                        self.app.gui.add_error_layer(db, error_line_layer)
                        msg = QCoreApplication.translate(
                            "LineQualityRules",
                            "A memory layer with {} overlapping boundaries (line intersections) has been added to the map."
                        ).format(error_line_layer.featureCount())
                    return msg, Qgis.Critical
Example #14
0
    def check_parcels_with_invalid_parcel_type_and_22_position_number(
            self, db):
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Logic.PARCEL_TYPE_AND_22_POSITION_OF_PARCEL_NUMBER)
        error_layer = QgsVectorLayer("NoGeometry", rule.error_table_name,
                                     "memory")
        pr = error_layer.dataProvider()
        pr.addAttributes(rule.error_table_fields)
        error_layer.updateFields()

        res, records = self.get_ladm_queries(
            db.engine
        ).get_parcels_with_invalid_parcel_type_and_22_position_number(db)

        new_features = list()
        if res:
            for record in records:
                error_code = None
                if record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_NO_HORIZONTAL_PROPERTY:
                    error_code = QUALITY_RULE_ERROR_CODE_E400905
                elif record[db.names.LC_PARCEL_T_PARCEL_TYPE_F] in (
                        LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_PARENT,
                        LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_PARCEL_UNIT):
                    error_code = QUALITY_RULE_ERROR_CODE_E400907
                elif record[db.names.LC_PARCEL_T_PARCEL_TYPE_F] in (
                        LADMNames.PARCEL_TYPE_CONDOMINIUM_PARENT,
                        LADMNames.PARCEL_TYPE_CONDOMINIUM_PARCEL_UNIT):
                    error_code = QUALITY_RULE_ERROR_CODE_E400902
                elif record[db.names.LC_PARCEL_T_PARCEL_TYPE_F] in (
                        LADMNames.PARCEL_TYPE_CEMETERY_PARENT,
                        LADMNames.PARCEL_TYPE_CEMETERY_PARCEL_UNIT):
                    error_code = QUALITY_RULE_ERROR_CODE_E400906
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_MEJORA:
                    error_code = QUALITY_RULE_ERROR_CODE_E400904
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_NO_HORIZONTAL_PROPERTY_MEJORA:
                    error_code = QUALITY_RULE_ERROR_CODE_E400903
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_ROAD:
                    error_code = QUALITY_RULE_ERROR_CODE_E400908
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_PUBLIC_USE:
                    error_code = QUALITY_RULE_ERROR_CODE_E400901

                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, QgsGeometry(), {
                        0: record[db.names.T_ILI_TID_F],
                        1: self.quality_rules_manager.get_error_message(
                            error_code),
                        2: error_code
                    })
                new_features.append(new_feature)

            error_layer.dataProvider().addFeatures(new_features)
        else:
            self.logger.error_msg(
                __name__, "Error executing query for rule {}: {}".format(
                    rule.rule_name, records))

        return self.return_message(db, rule.rule_name, error_layer)
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        target = self.parameterAsVectorLayer(parameters, self.OUTPUT, context)
        target.dataProvider().clearErrors()

        editable_before = False
        if target.isEditable():
            editable_before = True
            feedback.reportError(
                "\nWARNING: You need to close the edit session on layer '{}' before running this algorithm."
                .format(target.name()))
            return {self.OUTPUT: None}

        # Define a mapping between source and target layer
        mapping = dict()
        for target_idx in target.fields().allAttributesList():
            target_field = target.fields().field(target_idx)
            source_idx = source.fields().indexOf(target_field.name())
            if source_idx != -1:
                mapping[target_idx] = source_idx

        # Copy and Paste
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()
        destType = target.geometryType()
        destIsMulti = QgsWkbTypes.isMultiType(target.wkbType())

        #  Check if layer has Z or M values.
        drop_coordinates = list()
        add_coordinates = list()
        if QgsWkbTypes().hasM(source.wkbType()):
            # In ladm we don't use M values, so drop them if present
            drop_coordinates.append("M")
        if not QgsWkbTypes().hasZ(source.wkbType()) and QgsWkbTypes().hasZ(
                target.wkbType()):
            add_coordinates.append("Z")
        if QgsWkbTypes().hasZ(
                source.wkbType()) and not QgsWkbTypes().hasZ(target.wkbType()):
            drop_coordinates.append("Z")

        new_features = []
        display_target_geometry = QgsWkbTypes.displayString(target.wkbType())
        display_source_geometry = QgsWkbTypes.displayString(source.wkbType())

        for current, in_feature in enumerate(features):
            if feedback.isCanceled():
                break

            attrs = {
                target_idx: in_feature[source_idx]
                for target_idx, source_idx in mapping.items()
            }

            geom = QgsGeometry()

            if in_feature.hasGeometry() and target.isSpatial():
                # Convert geometry to match destination layer
                # Adapted from QGIS qgisapp.cpp, pasteFromClipboard()
                geom = in_feature.geometry()

                if destType != QgsWkbTypes.UnknownGeometry:
                    newGeometry = geom.convertToType(destType, destIsMulti)
                    if newGeometry.isNull():
                        feedback.reportError(
                            "\nERROR: Geometry type from the source layer ('{}') could not be converted to '{}'."
                            .format(display_source_geometry,
                                    display_target_geometry))
                        return {self.OUTPUT: None}
                    newGeometry = self.transform_geom(newGeometry,
                                                      drop_coordinates,
                                                      add_coordinates)
                    geom = newGeometry

                # Avoid intersection if enabled in digitize settings
                geom.avoidIntersections(
                    QgsProject.instance().avoidIntersectionsLayers())

            new_feature = QgsVectorLayerUtils().createFeature(
                target, geom, attrs)
            new_features.append(new_feature)

            feedback.setProgress(int(current * total))

        try:
            # This might print error messages... But, hey! That's what we want!
            res = target.dataProvider().addFeatures(new_features)
        except QgsEditError as e:
            if not editable_before:
                # Let's close the edit session to prepare for a next run
                target.rollBack()

            feedback.reportError(
                "\nERROR: No features could be copied into '{}', because of the following error:\n{}\n"
                .format(target.name(), repr(e)))
            return {self.OUTPUT: None}

        if res[0]:
            feedback.pushInfo(
                "\nSUCCESS: {} out of {} features from input layer were successfully copied into '{}'!"
                .format(len(new_features), source.featureCount(),
                        target.name()))
        else:
            if target.dataProvider().hasErrors():
                feedback.reportError(
                    "\nERROR: The data could not be copied! Details: {}.".
                    format(target.dataProvider().errors()[0]))
            else:
                feedback.reportError(
                    "\nERROR: The data could not be copied! No more details from the provider."
                )

        return {self.OUTPUT: target}
Example #16
0
    def test_field_is_read_only(self):
        """
        Test fieldIsReadOnly
        """
        layer = createLayerWithOnePoint()
        # layer is not editable => all fields are read only
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))

        layer.startEditing()
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))

        field = QgsField('test', QVariant.String)
        layer.addAttribute(field)
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))

        # simulate read-only field from provider
        field = QgsField('test2', QVariant.String)
        field.setReadOnly(True)
        layer.addAttribute(field)
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 3))

        layer.rollBack()
        layer.startEditing()

        # edit form config specifies read only
        form_config = layer.editFormConfig()
        form_config.setReadOnly(1, True)
        layer.setEditFormConfig(form_config)
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))
        form_config.setReadOnly(1, False)
        layer.setEditFormConfig(form_config)
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))

        # joined field
        layer2 = QgsVectorLayer(
            "Point?field=fldtxt2:string&field=fldint:integer", "addfeat",
            "memory")
        join_info = QgsVectorLayerJoinInfo()
        join_info.setJoinLayer(layer2)
        join_info.setJoinFieldName('fldint')
        join_info.setTargetFieldName('fldint')
        join_info.setUsingMemoryCache(True)
        layer.addJoin(join_info)
        layer.updateFields()

        self.assertEqual([f.name() for f in layer.fields()],
                         ['fldtxt', 'fldint', 'addfeat_fldtxt2'])
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 0))
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 1))
        # join layer is not editable
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))

        # make join editable
        layer.removeJoin(layer2.id())
        join_info.setEditable(True)
        layer.addJoin(join_info)
        layer.updateFields()
        self.assertEqual([f.name() for f in layer.fields()],
                         ['fldtxt', 'fldint', 'addfeat_fldtxt2'])

        # should still be read only -- the join layer itself is not editable
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))

        layer2.startEditing()
        self.assertFalse(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))

        # but now we set a property on the join layer which blocks editing for the feature...
        form_config = layer2.editFormConfig()
        form_config.setReadOnly(0, True)
        layer2.setEditFormConfig(form_config)
        # should now be read only -- the joined layer edit form config prohibits edits
        self.assertTrue(QgsVectorLayerUtils.fieldIsReadOnly(layer, 2))
Example #17
0
    def check_invalid_col_party_type_no_natural(self, db):
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Logic.COL_PARTY_NOT_NATURAL_TYPE)
        error_layer = QgsVectorLayer("NoGeometry", rule.error_table_name,
                                     "memory")
        pr = error_layer.dataProvider()
        pr.addAttributes(rule.error_table_fields)
        error_layer.updateFields()

        res, records = self.get_ladm_queries(
            db.engine).get_invalid_col_party_type_no_natural(db)

        new_features = list()
        if res:
            for record in records:
                if record[db.names.LC_PARTY_T_BUSINESS_NAME_F] > 0:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        error_layer, QgsGeometry(), {
                            0:
                            record[db.names.T_ILI_TID_F],
                            1:
                            self.quality_rules_manager.get_error_message(
                                QUALITY_RULE_ERROR_CODE_E400801),
                            2:
                            QUALITY_RULE_ERROR_CODE_E400801
                        })
                    new_features.append(new_feature)
                if record[db.names.LC_PARTY_T_SURNAME_1_F] > 0:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        error_layer, QgsGeometry(), {
                            0:
                            record[db.names.T_ILI_TID_F],
                            1:
                            self.quality_rules_manager.get_error_message(
                                QUALITY_RULE_ERROR_CODE_E400802),
                            2:
                            QUALITY_RULE_ERROR_CODE_E400802
                        })
                    new_features.append(new_feature)
                if record[db.names.LC_PARTY_T_FIRST_NAME_1_F] > 0:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        error_layer, QgsGeometry(), {
                            0:
                            record[db.names.T_ILI_TID_F],
                            1:
                            self.quality_rules_manager.get_error_message(
                                QUALITY_RULE_ERROR_CODE_E400803),
                            2:
                            QUALITY_RULE_ERROR_CODE_E400803
                        })
                    new_features.append(new_feature)
                if record[db.names.LC_PARTY_T_DOCUMENT_TYPE_F] > 0:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        error_layer, QgsGeometry(), {
                            0:
                            record[db.names.T_ILI_TID_F],
                            1:
                            self.quality_rules_manager.get_error_message(
                                QUALITY_RULE_ERROR_CODE_E400804),
                            2:
                            QUALITY_RULE_ERROR_CODE_E400804
                        })
                    new_features.append(new_feature)
            error_layer.dataProvider().addFeatures(new_features)
        else:
            self.logger.error_msg(
                __name__, "Error executing query for rule {}: {}".format(
                    rule.rule_name, records))

        return self.return_message(db, rule.rule_name, error_layer)
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPointXY(QgsPointXY(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().asWkt(), g.asWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        #adjusted so that input value and output feature are the same
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueDefinition(2, QgsDefaultValue(None))

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])

        # test with violated unique constraints and default value expression providing unique value
        layer.setDefaultValueDefinition(1, QgsDefaultValue('130'))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        self.assertEqual(f.attributes(), ['test_4', 130, NULL])
        # fallback: test with violated unique constraints and default value expression providing already existing value
        # add the feature with the default value:
        self.assertTrue(layer.dataProvider().addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        # and since the default value providing an already existing value (130) it generates a unique value (next int: 131)
        self.assertEqual(f.attributes(), ['test_5', 131, NULL])
        layer.setDefaultValueDefinition(1, QgsDefaultValue(None))

        # test with manually correct unique constraint
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 132})
        self.assertEqual(f.attributes(), ['test_5', 132, NULL])

        """ test creating a feature respecting unique values of postgres provider """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")

        # init connection string
        dbconn = 'dbname=\'qgis_test\''
        if 'QGIS_PGTEST_DB' in os.environ:
            dbconn = os.environ['QGIS_PGTEST_DB']

        # create a vector layer
        pg_layer = QgsVectorLayer('{} table="qgis_test"."authors" sql='.format(dbconn), "authors", "postgres")
        self.assertTrue(pg_layer.isValid())
        # check the default clause
        default_clause = 'nextval(\'qgis_test.authors_pk_seq\'::regclass)'
        self.assertEqual(pg_layer.dataProvider().defaultValueClause(0), default_clause)

        # though default_clause is after the first create not unique (until save), it should fill up all the features with it
        pg_layer.startEditing()
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        self.assertTrue(QgsVectorLayerUtils.valueExists(pg_layer, 0, default_clause))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        # if a unique value is passed, use it
        f = QgsVectorLayerUtils.createFeature(pg_layer, attributes={0: 40, 1: NULL})
        self.assertEqual(f.attributes(), [40, NULL])
        # and if a default value is configured use it as well
        pg_layer.setDefaultValueDefinition(0, QgsDefaultValue('11*4'))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [44, NULL])
        pg_layer.rollBack()
Example #19
0
    def test_validate_attribute(self):
        """ test validating attributes against constraints """
        layer = createLayerWithOnePoint()

        # field expression check
        layer.setConstraintExpression(1, 'fldint>5')

        f = QgsFeature(2)
        f.setAttributes(["test123", 6])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        f.setAttributes(["test123", 2])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)
        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(
            layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # bad field expression check
        layer.setConstraintExpression(1, 'fldint>')
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        layer.setConstraintExpression(1, None)

        # not null constraint
        f.setAttributes(["test123", NULL])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(
            layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # unique constraint
        f.setAttributes(["test123", 123])
        layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(
            layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # checking only for soft constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique,
                                 QgsFieldConstraints.ConstraintStrengthHard)
        res, errors = QgsVectorLayerUtils.validateAttribute(
            layer, f, 1, strength=QgsFieldConstraints.ConstraintStrengthSoft)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        # checking for hard constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(
            layer, f, 1, strength=QgsFieldConstraints.ConstraintStrengthHard)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)

        # check - same id should be ignored when testing for uniqueness
        f1 = QgsFeature(1)
        f1.setAttributes(["test123", 123])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f1, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # test double constraint failure
        layer.setConstraintExpression(1, 'fldint>5')
        layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        f.setAttributes(["test123", NULL])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 2)
        print(errors)
    def fill_right_of_way_relations(self, db):
        layers = {
            self.names.OP_ADMINISTRATIVE_SOURCE_T: {'name': self.names.OP_ADMINISTRATIVE_SOURCE_T, 'geometry': None, LAYER: None},
            self.names.OP_PARCEL_T: {'name': self.names.OP_PARCEL_T, 'geometry': None, LAYER: None},
            self.names.OP_PLOT_T: {'name': self.names.OP_PLOT_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None},
            self.names.OP_RESTRICTION_T: {'name': self.names.OP_RESTRICTION_T, 'geometry': None, LAYER: None},
            self.names.OP_RESTRICTION_TYPE_D: {'name': self.names.OP_RESTRICTION_TYPE_D, 'geometry': None, LAYER: None},
            self.names.OP_RIGHT_OF_WAY_T: {'name': self.names.OP_RIGHT_OF_WAY_T, 'geometry': QgsWkbTypes.PolygonGeometry, LAYER: None},
            self.names.COL_RRR_SOURCE_T: {'name': self.names.COL_RRR_SOURCE_T, 'geometry': None, LAYER: None},
            self.names.OP_SURVEY_POINT_T: {'name': self.names.OP_SURVEY_POINT_T, 'geometry': None, LAYER: None},
            self.names.COL_UE_BAUNIT_T: {'name': self.names.COL_UE_BAUNIT_T, 'geometry': None, LAYER: None}
        }

        # Load layers
        self.qgis_utils.get_layers(db, layers, load=True)
        if not layers:
            return None

        exp = "\"{}\" = '{}'".format(self.names.ILICODE_F, LADMNames.RESTRICTION_TYPE_D_RIGHT_OF_WAY_ILICODE_VALUE)
        restriction_right_of_way_t_id = [feature for feature in layers[self.names.OP_RESTRICTION_TYPE_D][LAYER].getFeatures(exp)][0][self.names.T_ID_F]

        if layers[self.names.OP_PLOT_T][LAYER].selectedFeatureCount() == 0 or layers[self.names.OP_RIGHT_OF_WAY_T][LAYER].selectedFeatureCount() == 0 or layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER].selectedFeatureCount() == 0:
            if self.qgis_utils.get_layer_from_layer_tree(db, self.names.OP_PLOT_T, geometry_type=QgsWkbTypes.PolygonGeometry) is None:
                self.logger.message_with_button_load_layer_emitted.emit(
                    QCoreApplication.translate("RightOfWay",
                                               "First load the layer {} into QGIS and select at least one plot!").format(self.names.OP_PLOT_T),
                    QCoreApplication.translate("RightOfWay", "Load layer {} now").format(self.names.OP_PLOT_T),
                    self.names.OP_PLOT_T,
                    Qgis.Warning)
            else:
                self.logger.warning_msg(__name__, QCoreApplication.translate("RightOfWay",
                    "Select at least one benefited plot, one right of way and at least one administrative source to create relations!"))
                return
        else:
            ue_baunit_features = layers[self.names.COL_UE_BAUNIT_T][LAYER].getFeatures()
            # Get unique pairs id_right_of_way-id_parcel
            existing_pairs = [(ue_baunit_feature[self.names.COL_UE_BAUNIT_T_PARCEL_F], ue_baunit_feature[self.names.COL_UE_BAUNIT_T_OP_RIGHT_OF_WAY_F]) for ue_baunit_feature in ue_baunit_features]
            existing_pairs = set(existing_pairs)

            plot_ids = [f[self.names.T_ID_F] for f in layers[self.names.OP_PLOT_T][LAYER].selectedFeatures()]

            right_of_way_id = layers[self.names.OP_RIGHT_OF_WAY_T][LAYER].selectedFeatures()[0].attribute(self.names.T_ID_F)
            id_pairs = list()
            for plot in plot_ids:
                exp = "\"{uebaunit}\" = {plot}".format(uebaunit=self.names.COL_UE_BAUNIT_T_OP_PLOT_F, plot=plot)
                parcels = layers[self.names.COL_UE_BAUNIT_T][LAYER].getFeatures(exp)
                for parcel in parcels:
                    id_pair = (parcel.attribute(self.names.COL_UE_BAUNIT_T_PARCEL_F), right_of_way_id)
                    id_pairs.append(id_pair)

            if len(id_pairs) < len(plot_ids):
                # If any relationship plot-parcel is not found, we don't need to continue
                self.qlogger.warning_msg(__name__, QCoreApplication.translate("RightOfWay",
                    "One or more pairs id_plot-id_parcel weren't found, this is needed to create benefited and restriction relations."))
                return

            if id_pairs:
                new_features = list()
                for id_pair in id_pairs:
                    if not id_pair in existing_pairs:
                        #Create feature
                        new_feature = QgsVectorLayerUtils().createFeature(layers[self.names.COL_UE_BAUNIT_T][LAYER])
                        new_feature.setAttribute(self.names.COL_UE_BAUNIT_T_PARCEL_F, id_pair[0])
                        new_feature.setAttribute(self.names.COL_UE_BAUNIT_T_OP_RIGHT_OF_WAY_F, id_pair[1])
                        self.logger.info(__name__, "Saving RightOfWay-Parcel: {}-{}".format(id_pair[1], id_pair[0]))
                        new_features.append(new_feature)

                layers[self.names.COL_UE_BAUNIT_T][LAYER].dataProvider().addFeatures(new_features)
                self.logger.info_msg(__name__, QCoreApplication.translate("RightOfWay",
                    "{} out of {} records were saved into {}! {} out of {} records already existed in the database.").format(
                        len(new_features),
                        len(id_pairs),
                        self.names.COL_UE_BAUNIT_T,
                        len(id_pairs) - len(new_features),
                        len(id_pairs)
                    ))

            spatial_join_layer = processing.run("qgis:joinattributesbylocation",
                                                {
                                                    'INPUT': layers[self.names.OP_PLOT_T][LAYER],
                                                    'JOIN': QgsProcessingFeatureSourceDefinition(layers[self.names.OP_RIGHT_OF_WAY_T][LAYER].id(), True),
                                                    'PREDICATE': [0],
                                                    'JOIN_FIELDS': [self.names.T_ID_F],
                                                    'METHOD': 0,
                                                    'DISCARD_NONMATCHING': True,
                                                    'PREFIX': '',
                                                    'OUTPUT': 'memory:'})['OUTPUT']

            restriction_features = layers[self.names.OP_RESTRICTION_T][LAYER].getFeatures()
            existing_restriction_pairs = [(restriction_feature[self.names.COL_BAUNIT_RRR_T_UNIT_F], restriction_feature[self.names.COL_RRR_T_DESCRIPTION_F]) for restriction_feature in restriction_features]
            existing_restriction_pairs = set(existing_restriction_pairs)
            id_pairs_restriction = list()
            plot_ids = spatial_join_layer.getFeatures()

            for plot in plot_ids:
                exp = "\"uebaunit\" = {plot}".format(uebaunit=self.names.COL_UE_BAUNIT_T_OP_PLOT_F, plot=plot.attribute(self.names.T_ID_F))
                parcels = layers[self.names.COL_UE_BAUNIT_T][LAYER].getFeatures(exp)
                for parcel in parcels:
                    id_pair_restriction = (parcel.attribute(self.names.COL_UE_BAUNIT_T_PARCEL_F), QCoreApplication.translate("RightOfWay", "Right of way"))
                    id_pairs_restriction.append(id_pair_restriction)

            new_restriction_features = list()
            if id_pairs_restriction:
                for id_pair in id_pairs_restriction:
                    if not id_pair in existing_restriction_pairs:
                        #Create feature
                        new_feature = QgsVectorLayerUtils().createFeature(layers[self.names.OP_RESTRICTION_T][LAYER])
                        new_feature.setAttribute(self.names.COL_BAUNIT_RRR_T_UNIT_F, id_pair[0])
                        new_feature.setAttribute(self.names.COL_RRR_T_DESCRIPTION_F, id_pair[1])
                        new_feature.setAttribute(self.names.OP_RESTRICTION_T_TYPE_F, restriction_right_of_way_t_id)
                        self.logger.info(__name__, "Saving RightOfWay-Parcel: {}-{}".format(id_pair[1], id_pair[0]))
                        new_restriction_features.append(new_feature)

                layers[self.names.OP_RESTRICTION_T][LAYER].dataProvider().addFeatures(new_restriction_features)
                self.logger.info_msg(__name__, QCoreApplication.translate("RightOfWay",
                    "{} out of {} records were saved into {}! {} out of {} records already existed in the database.").format(
                        len(new_restriction_features),
                        len(id_pairs_restriction),
                        self.names.OP_RESTRICTION_T,
                        len(id_pairs_restriction) - len(new_restriction_features),
                        len(id_pairs_restriction)
                    ))

            administrative_source_ids = [f[self.names.T_ID_F] for f in layers[self.names.OP_ADMINISTRATIVE_SOURCE_T][LAYER].selectedFeatures()]

            source_relation_features = layers[self.names.COL_RRR_SOURCE_T][LAYER].getFeatures()

            existing_source_pairs = [(source_relation_feature[self.names.COL_RRR_SOURCE_T_SOURCE_F], source_relation_feature[self.names.COL_RRR_SOURCE_T_OP_RESTRICTION_F]) for source_relation_feature in source_relation_features]
            existing_source_pairs = set(existing_source_pairs)

            rrr_source_relation_pairs = list()

            for administrative_source_id in administrative_source_ids:
                for restriction_feature in new_restriction_features:
                    rrr_source_relation_pair = (administrative_source_id, restriction_feature.attribute(self.names.T_ID_F))
                    rrr_source_relation_pairs.append(rrr_source_relation_pair)

            new_rrr_source_relation_features = list()
            if rrr_source_relation_pairs:
                for id_pair in rrr_source_relation_pairs:
                    if not id_pair in existing_source_pairs:
                        new_feature = QgsVectorLayerUtils().createFeature(layers[self.names.COL_RRR_SOURCE_T][LAYER])
                        new_feature.setAttribute(self.names.COL_RRR_SOURCE_T_SOURCE_F, id_pair[0])
                        new_feature.setAttribute(self.names.COL_RRR_SOURCE_T_OP_RESTRICTION_F, id_pair[1])
                        self.logger.info(__name__, "Saving Restriction-Source: {}-{}".format(id_pair[1], id_pair[0]))
                        new_rrr_source_relation_features.append(new_feature)

                layers[self.names.COL_RRR_SOURCE_T][LAYER].dataProvider().addFeatures(new_rrr_source_relation_features)
                self.logger.info_msg(__name__, QCoreApplication.translate("RightOfWay",
                    "{} out of {} records were saved into {}! {} out of {} records already existed in the database.").format(
                        len(new_rrr_source_relation_features),
                        len(rrr_source_relation_pairs),
                        self.names.COL_RRR_SOURCE_T,
                        len(rrr_source_relation_pairs) - len(new_rrr_source_relation_features),
                        len(rrr_source_relation_pairs)
                    ))
Example #21
0
    def finish_feature_creation(self, layerId, features):
        message = QCoreApplication.translate(self.WIZARD_NAME,
                                             "'{}' tool has been closed because an error occurred while trying to save the data.").format(self.WIZARD_TOOL_NAME)

        if len(features) != 1:
            message = QCoreApplication.translate(self.WIZARD_NAME,
                                                 "'{}' tool has been closed. We should have got only one spatial source... We cannot do anything with {} spatial sources").format(self.WIZARD_TOOL_NAME, len(features))
            self.log.logMessage("We should have got only one spatial source... We cannot do anything with {} spatial sources".format(len(features)), PLUGIN_NAME, Qgis.Warning)
        else:

            feature = features[0]
            feature_ids_dict = dict()

            if self._layers[PLOT_TABLE][LAYER] is not None:
                if self._layers[PLOT_TABLE][LAYER].selectedFeatureCount() > 0:
                    feature_ids_dict[PLOT_TABLE] = [f[ID_FIELD] for f in self._layers[PLOT_TABLE][LAYER].selectedFeatures()]

            if self._layers[BOUNDARY_TABLE][LAYER] is not None:
                if self._layers[BOUNDARY_TABLE][LAYER].selectedFeatureCount() > 0:
                    feature_ids_dict[BOUNDARY_TABLE] = [f[ID_FIELD] for f in self._layers[BOUNDARY_TABLE][LAYER].selectedFeatures()]

            if self._layers[BOUNDARY_POINT_TABLE][LAYER] is not None:
                if self._layers[BOUNDARY_POINT_TABLE][LAYER].selectedFeatureCount() > 0:
                    feature_ids_dict[BOUNDARY_POINT_TABLE] = [f[ID_FIELD] for f in self._layers[BOUNDARY_POINT_TABLE][LAYER].selectedFeatures()]

            if self._layers[SURVEY_POINT_TABLE][LAYER] is not None:
                if self._layers[SURVEY_POINT_TABLE][LAYER].selectedFeatureCount() > 0:
                    feature_ids_dict[SURVEY_POINT_TABLE] = [f[ID_FIELD] for f in self._layers[SURVEY_POINT_TABLE][LAYER].selectedFeatures()]

            if self._layers[CONTROL_POINT_TABLE][LAYER] is not None:
                if self._layers[CONTROL_POINT_TABLE][LAYER].selectedFeatureCount() > 0:
                    feature_ids_dict[CONTROL_POINT_TABLE] = [f[ID_FIELD] for f in self._layers[CONTROL_POINT_TABLE][LAYER].selectedFeatures()]

            if not feature.isValid():
                self.log.logMessage("Feature not found in layer Spatial Source...", PLUGIN_NAME, Qgis.Warning)
            else:
                spatial_source_id = feature[ID_FIELD]
                all_new_features = list()

                # Fill association table, depending on the case
                new_features = list()
                if PLOT_TABLE in feature_ids_dict:
                    # Fill uesource table
                    for plot_id in feature_ids_dict[PLOT_TABLE]:
                        new_feature = QgsVectorLayerUtils().createFeature(self._layers[UESOURCE_TABLE][LAYER])
                        new_feature.setAttribute(UESOURCE_TABLE_PLOT_FIELD, plot_id)
                        new_feature.setAttribute(UESOURCE_TABLE_SOURCE_FIELD, spatial_source_id)
                        self.log.logMessage("Saving Plot-SpatialSource: {}-{}".format(plot_id, spatial_source_id), PLUGIN_NAME, Qgis.Info)
                        new_features.append(new_feature)

                    self._layers[UESOURCE_TABLE][LAYER].dataProvider().addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if BOUNDARY_TABLE in feature_ids_dict:
                    # Fill cclsource table
                    for boundary_id in feature_ids_dict[BOUNDARY_TABLE]:
                        new_feature = QgsVectorLayerUtils().createFeature(self._layers[CCLSOURCE_TABLE][LAYER])
                        new_feature.setAttribute(CCLSOURCE_TABLE_BOUNDARY_FIELD, boundary_id)
                        new_feature.setAttribute(CCLSOURCE_TABLE_SOURCE_FIELD, spatial_source_id)
                        self.log.logMessage("Saving Boundary-SpatialSource: {}-{}".format(boundary_id, spatial_source_id), PLUGIN_NAME, Qgis.Info)
                        new_features.append(new_feature)

                    self._layers[CCLSOURCE_TABLE][LAYER].dataProvider().addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if BOUNDARY_POINT_TABLE in feature_ids_dict:
                    for boundary_point_id in feature_ids_dict[BOUNDARY_POINT_TABLE]:
                        new_feature = QgsVectorLayerUtils().createFeature(self._layers[POINTSOURCE_TABLE][LAYER])
                        new_feature.setAttribute(POINTSOURCE_TABLE_BOUNDARYPOINT_FIELD, boundary_point_id)
                        new_feature.setAttribute(POINTSOURCE_TABLE_SOURCE_FIELD, spatial_source_id)
                        self.log.logMessage("Saving BoundaryPoint-SpatialSource: {}-{}".format(boundary_point_id, spatial_source_id), PLUGIN_NAME, Qgis.Info)
                        new_features.append(new_feature)

                    self._layers[POINTSOURCE_TABLE][LAYER].dataProvider().addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if SURVEY_POINT_TABLE in feature_ids_dict:
                    for survey_point_id in feature_ids_dict[SURVEY_POINT_TABLE]:
                        new_feature = QgsVectorLayerUtils().createFeature(self._layers[POINTSOURCE_TABLE][LAYER])
                        new_feature.setAttribute(POINTSOURCE_TABLE_SURVEYPOINT_FIELD, survey_point_id)
                        new_feature.setAttribute(POINTSOURCE_TABLE_SOURCE_FIELD, spatial_source_id)
                        self.log.logMessage("Saving SurveyPoint-SpatialSource: {}-{}".format(survey_point_id, spatial_source_id), PLUGIN_NAME, Qgis.Info)
                        new_features.append(new_feature)

                    self._layers[POINTSOURCE_TABLE][LAYER].dataProvider().addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if CONTROL_POINT_TABLE in feature_ids_dict:
                    for control_point_id in feature_ids_dict[CONTROL_POINT_TABLE]:
                        new_feature = QgsVectorLayerUtils().createFeature(self._layers[POINTSOURCE_TABLE][LAYER])
                        new_feature.setAttribute(POINTSOURCE_TABLE_CONTROLPOINT_FIELD, control_point_id)
                        new_feature.setAttribute(POINTSOURCE_TABLE_SOURCE_FIELD, spatial_source_id)
                        self.log.logMessage("Saving ControlPoint-SpatialSource: {}-{}".format(control_point_id, spatial_source_id), PLUGIN_NAME, Qgis.Info)
                        new_features.append(new_feature)

                    self._layers[POINTSOURCE_TABLE][LAYER].dataProvider().addFeatures(new_features)
                    all_new_features.extend(new_feature)

                if all_new_features:
                    message = QCoreApplication.translate(self.WIZARD_NAME,
                                                   "The new spatial source (t_id={}) was successfully created and associated with the following features: {}").format(spatial_source_id, feature_ids_dict)
                else:
                    message = QCoreApplication.translate(self.WIZARD_NAME,
                                                   "The new spatial source (t_id={}) was successfully created and it wasn't associated with a spatial unit").format(spatial_source_id)

        self._layers[self.EDITING_LAYER_NAME][LAYER].committedFeaturesAdded.disconnect()
        self.log.logMessage("Spatial Source's committedFeaturesAdded SIGNAL disconnected", PLUGIN_NAME, Qgis.Info)
        self.close_wizard(message)
    def post_save(self, features):
        message = QCoreApplication.translate(
            "WizardTranslations",
            "'{}' tool has been closed because an error occurred while trying to save the data."
        ).format(self.WIZARD_TOOL_NAME)
        if len(features) != 1:
            message = QCoreApplication.translate(
                "WizardTranslations",
                "'{}' tool has been closed. We should have got only one {} by we have {}"
            ).format(self.WIZARD_TOOL_NAME, self.WIZARD_FEATURE_NAME,
                     len(features))
            self.logger.warning(
                __name__,
                "We should have got only one {}, but we have {}".format(
                    self.WIZARD_FEATURE_NAME, len(features)))
        else:
            feature = features[0]
            feature_ids_dict = dict()

            if self._layers[self.names.LC_PLOT_T] is not None:
                if self._layers[
                        self.names.LC_PLOT_T].selectedFeatureCount() > 0:
                    feature_ids_dict[self.names.LC_PLOT_T] = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_PLOT_T].selectedFeatures()
                    ]

            if self._layers[self.names.LC_BOUNDARY_T] is not None:
                if self._layers[
                        self.names.LC_BOUNDARY_T].selectedFeatureCount() > 0:
                    feature_ids_dict[self.names.LC_BOUNDARY_T] = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_BOUNDARY_T].selectedFeatures()
                    ]

            if self._layers[self.names.LC_BOUNDARY_POINT_T] is not None:
                if self._layers[
                        self.names.LC_BOUNDARY_POINT_T].selectedFeatureCount(
                        ) > 0:
                    feature_ids_dict[self.names.LC_BOUNDARY_POINT_T] = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_BOUNDARY_POINT_T].selectedFeatures()
                    ]

            if self._layers[self.names.LC_SURVEY_POINT_T] is not None:
                if self._layers[self.names.
                                LC_SURVEY_POINT_T].selectedFeatureCount() > 0:
                    feature_ids_dict[self.names.LC_SURVEY_POINT_T] = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_SURVEY_POINT_T].selectedFeatures()
                    ]

            if self._layers[self.names.LC_CONTROL_POINT_T] is not None:
                if self._layers[self.names.
                                LC_CONTROL_POINT_T].selectedFeatureCount() > 0:
                    feature_ids_dict[self.names.LC_CONTROL_POINT_T] = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_CONTROL_POINT_T].selectedFeatures()
                    ]

            if not feature.isValid():
                self.logger.warning(
                    __name__, "Feature not found in layer Spatial Source...")
            else:
                spatial_source_id = feature[self.names.T_ID_F]
                all_new_features = list()

                # Fill association table, depending on the case
                new_features = list()
                if self.names.LC_PLOT_T in feature_ids_dict:
                    # Fill uesource table
                    for plot_id in feature_ids_dict[self.names.LC_PLOT_T]:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            self._layers[self.names.COL_UE_SOURCE_T])
                        new_feature.setAttribute(
                            self.names.COL_UE_SOURCE_T_LC_PLOT_F, plot_id)
                        new_feature.setAttribute(
                            self.names.COL_UE_SOURCE_T_SOURCE_F,
                            spatial_source_id)
                        self.logger.info(
                            __name__,
                            "Saving Plot-SpatialSource: {}-{}".format(
                                plot_id, spatial_source_id))
                        new_features.append(new_feature)

                    self._layers[self.names.COL_UE_SOURCE_T].dataProvider(
                    ).addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if self.names.LC_BOUNDARY_T in feature_ids_dict:
                    # Fill cclsource table
                    for boundary_id in feature_ids_dict[
                            self.names.LC_BOUNDARY_T]:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            self._layers[self.names.COL_CCL_SOURCE_T])

                        # Todo: Update when ili2db issue is solved.
                        # Todo: When an abstract class only implements a concrete class, the name of the attribute is different if two or more classes are implemented.
                        new_feature.setAttribute(
                            self.names.COL_CCL_SOURCE_T_BOUNDARY_F,
                            boundary_id)
                        new_feature.setAttribute(
                            self.names.COL_CCL_SOURCE_T_SOURCE_F,
                            spatial_source_id)
                        self.logger.info(
                            __name__,
                            "Saving Boundary-SpatialSource: {}-{}".format(
                                boundary_id, spatial_source_id))
                        new_features.append(new_feature)

                    self._layers[self.names.COL_CCL_SOURCE_T].dataProvider(
                    ).addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if self.names.LC_BOUNDARY_POINT_T in feature_ids_dict:
                    for boundary_point_id in feature_ids_dict[
                            self.names.LC_BOUNDARY_POINT_T]:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            self._layers[self.names.COL_POINT_SOURCE_T])
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_LC_BOUNDARY_POINT_F,
                            boundary_point_id)
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_SOURCE_F,
                            spatial_source_id)
                        self.logger.info(
                            __name__,
                            "Saving BoundaryPoint-SpatialSource: {}-{}".format(
                                boundary_point_id, spatial_source_id))
                        new_features.append(new_feature)

                    self._layers[self.names.COL_POINT_SOURCE_T].dataProvider(
                    ).addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if self.names.LC_SURVEY_POINT_T in feature_ids_dict:
                    for survey_point_id in feature_ids_dict[
                            self.names.LC_SURVEY_POINT_T]:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            self._layers[self.names.COL_POINT_SOURCE_T])
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_LC_SURVEY_POINT_F,
                            survey_point_id)
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_SOURCE_F,
                            spatial_source_id)
                        self.logger.info(
                            __name__,
                            "Saving SurveyPoint-SpatialSource: {}-{}".format(
                                survey_point_id, spatial_source_id))
                        new_features.append(new_feature)

                    self._layers[self.names.COL_POINT_SOURCE_T].dataProvider(
                    ).addFeatures(new_features)
                    all_new_features.extend(new_feature)

                new_features = list()
                if self.names.LC_CONTROL_POINT_T in feature_ids_dict:
                    for control_point_id in feature_ids_dict[
                            self.names.LC_CONTROL_POINT_T]:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            self._layers[self.names.COL_POINT_SOURCE_T])
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_LC_CONTROL_POINT_F,
                            control_point_id)
                        new_feature.setAttribute(
                            self.names.COL_POINT_SOURCE_T_SOURCE_F,
                            spatial_source_id)
                        self.logger.info(
                            __name__,
                            "Saving ControlPoint-SpatialSource: {}-{}".format(
                                control_point_id, spatial_source_id))
                        new_features.append(new_feature)

                    self._layers[self.names.COL_POINT_SOURCE_T].dataProvider(
                    ).addFeatures(new_features)
                    all_new_features.extend(new_feature)

                if all_new_features:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new spatial source (t_id={}) was successfully created and associated with the following features: {}"
                    ).format(spatial_source_id, feature_ids_dict)
                else:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new spatial source (t_id={}) was successfully created and it wasn't associated with a spatial unit"
                    ).format(spatial_source_id)

        return message
Example #23
0
    def fill_right_of_way_relations(self, db):
        layers = {
            ADMINISTRATIVE_SOURCE_TABLE: {
                'name': ADMINISTRATIVE_SOURCE_TABLE,
                'geometry': None,
                LAYER: None
            },
            PARCEL_TABLE: {
                'name': PARCEL_TABLE,
                'geometry': None,
                LAYER: None
            },
            PLOT_TABLE: {
                'name': PLOT_TABLE,
                'geometry': QgsWkbTypes.PolygonGeometry,
                LAYER: None
            },
            RESTRICTION_TABLE: {
                'name': RESTRICTION_TABLE,
                'geometry': None,
                LAYER: None
            },
            RIGHT_OF_WAY_TABLE: {
                'name': RIGHT_OF_WAY_TABLE,
                'geometry': QgsWkbTypes.PolygonGeometry,
                LAYER: None
            },
            RRR_SOURCE_RELATION_TABLE: {
                'name': RRR_SOURCE_RELATION_TABLE,
                'geometry': None,
                LAYER: None
            },
            SURVEY_POINT_TABLE: {
                'name': SURVEY_POINT_TABLE,
                'geometry': None,
                LAYER: None
            },
            UEBAUNIT_TABLE: {
                'name': UEBAUNIT_TABLE,
                'geometry': None,
                LAYER: None
            }
        }

        # Load layers
        self.qgis_utils.get_layers(db, layers, load=True)
        if not layers:
            return None

        if layers[PLOT_TABLE][LAYER].selectedFeatureCount(
        ) == 0 or layers[RIGHT_OF_WAY_TABLE][LAYER].selectedFeatureCount(
        ) == 0 or layers[ADMINISTRATIVE_SOURCE_TABLE][
                LAYER].selectedFeatureCount() == 0:
            if self.qgis_utils.get_layer_from_layer_tree(
                    db, PLOT_TABLE,
                    geometry_type=QgsWkbTypes.PolygonGeometry) is None:
                self.qgis_utils.message_with_button_load_layer_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "First load the layer {} into QGIS and select at least one plot!"
                    ).format(PLOT_TABLE),
                    QCoreApplication.translate(
                        "RightOfWay", "Load layer {} now").format(PLOT_TABLE),
                    [PLOT_TABLE, None], Qgis.Warning)
            else:
                self.qgis_utils.message_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "Select at least one benefited plot, one right of way and at least one administrative source to create relations!"
                    ), Qgis.Warning)
                return
        else:
            ue_baunit_features = layers[UEBAUNIT_TABLE][LAYER].getFeatures()
            # Get unique pairs id_right_of_way-id_parcel
            existing_pairs = [
                (ue_baunit_feature[UEBAUNIT_TABLE_PARCEL_FIELD],
                 ue_baunit_feature[UEBAUNIT_TABLE_RIGHT_OF_WAY_FIELD])
                for ue_baunit_feature in ue_baunit_features
            ]
            existing_pairs = set(existing_pairs)

            plot_ids = [
                f[ID_FIELD]
                for f in layers[PLOT_TABLE][LAYER].selectedFeatures()
            ]

            right_of_way_id = layers[RIGHT_OF_WAY_TABLE][
                LAYER].selectedFeatures()[0].attribute(ID_FIELD)
            id_pairs = list()
            for plot in plot_ids:
                exp = "\"{uebaunit}\" = {plot}".format(
                    uebaunit=UEBAUNIT_TABLE_PLOT_FIELD, plot=plot)
                parcels = layers[UEBAUNIT_TABLE][LAYER].getFeatures(exp)
                for parcel in parcels:
                    id_pair = (parcel.attribute(UEBAUNIT_TABLE_PARCEL_FIELD),
                               right_of_way_id)
                    id_pairs.append(id_pair)

            if len(id_pairs) < len(plot_ids):
                # If any relationship plot-parcel is not found, we don't need to continue
                self.qgis_utils.message_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "One or more pairs id_plot-id_parcel weren't found, this is needed to create benefited and restriction relations."
                    ), Qgis.Warning)
                return

            if id_pairs:
                new_features = list()
                for id_pair in id_pairs:
                    if not id_pair in existing_pairs:
                        #Create feature
                        new_feature = QgsVectorLayerUtils().createFeature(
                            layers[UEBAUNIT_TABLE][LAYER])
                        new_feature.setAttribute(UEBAUNIT_TABLE_PARCEL_FIELD,
                                                 id_pair[0])
                        new_feature.setAttribute(
                            UEBAUNIT_TABLE_RIGHT_OF_WAY_FIELD, id_pair[1])
                        self.log.logMessage(
                            "Saving RightOfWay-Parcel: {}-{}".format(
                                id_pair[1], id_pair[0]), PLUGIN_NAME,
                            Qgis.Info)
                        new_features.append(new_feature)

                layers[UEBAUNIT_TABLE][LAYER].dataProvider().addFeatures(
                    new_features)
                self.qgis_utils.message_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "{} out of {} records were saved into {}! {} out of {} records already existed in the database."
                    ).format(len(new_features), len(id_pairs), UEBAUNIT_TABLE,
                             len(id_pairs) - len(new_features), len(id_pairs)),
                    Qgis.Info)

            spatial_join_layer = processing.run(
                "qgis:joinattributesbylocation", {
                    'INPUT':
                    layers[PLOT_TABLE][LAYER],
                    'JOIN':
                    QgsProcessingFeatureSourceDefinition(
                        layers[RIGHT_OF_WAY_TABLE][LAYER].id(), True),
                    'PREDICATE': [0],
                    'JOIN_FIELDS':
                    [ID_FIELD, RIGHT_OF_WAY_TABLE_IDENTIFICATOR_FIELD],
                    'METHOD':
                    0,
                    'DISCARD_NONMATCHING':
                    True,
                    'PREFIX':
                    '',
                    'OUTPUT':
                    'memory:'
                })['OUTPUT']

            restriction_features = layers[RESTRICTION_TABLE][
                LAYER].getFeatures()
            existing_restriction_pairs = [
                (restriction_feature[RESTRICTION_TABLE_PARCEL_FIELD],
                 restriction_feature[RESTRICTION_TABLE_DESCRIPTION_FIELD])
                for restriction_feature in restriction_features
            ]
            existing_restriction_pairs = set(existing_restriction_pairs)
            id_pairs_restriction = list()
            plot_ids = spatial_join_layer.getFeatures()

            for plot in plot_ids:
                exp = "\"uebaunit\" = {plot}".format(
                    uebaunit=UEBAUNIT_TABLE_PLOT_FIELD,
                    plot=plot.attribute(ID_FIELD))
                parcels = layers[UEBAUNIT_TABLE][LAYER].getFeatures(exp)
                for parcel in parcels:
                    id_pair_restriction = (
                        parcel.attribute(UEBAUNIT_TABLE_PARCEL_FIELD),
                        "Asociada a la servidumbre {}".format(
                            plot.attribute(
                                RIGHT_OF_WAY_TABLE_IDENTIFICATOR_FIELD)))
                    id_pairs_restriction.append(id_pair_restriction)

            new_restriction_features = list()
            if id_pairs_restriction:
                for id_pair in id_pairs_restriction:
                    if not id_pair in existing_restriction_pairs:
                        #Create feature
                        new_feature = QgsVectorLayerUtils().createFeature(
                            layers[RESTRICTION_TABLE][LAYER])
                        new_feature.setAttribute(
                            RESTRICTION_TABLE_PARCEL_FIELD, id_pair[0])
                        new_feature.setAttribute(
                            RESTRICTION_TABLE_DESCRIPTION_FIELD, id_pair[1])
                        new_feature.setAttribute(
                            TYPE_FIELD,
                            COL_RESTRICTION_TYPE_RIGHT_OF_WAY_VALUE)
                        self.log.logMessage(
                            "Saving RightOfWay-Parcel: {}-{}".format(
                                id_pair[1], id_pair[0]), PLUGIN_NAME,
                            Qgis.Info)
                        new_restriction_features.append(new_feature)

                layers[RESTRICTION_TABLE][LAYER].dataProvider().addFeatures(
                    new_restriction_features)
                self.qgis_utils.message_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "{} out of {} records were saved into {}! {} out of {} records already existed in the database."
                    ).format(
                        len(new_restriction_features),
                        len(id_pairs_restriction), RESTRICTION_TABLE,
                        len(id_pairs_restriction) -
                        len(new_restriction_features),
                        len(id_pairs_restriction)), Qgis.Info)

            administrative_source_ids = [
                f[ID_FIELD] for f in layers[ADMINISTRATIVE_SOURCE_TABLE]
                [LAYER].selectedFeatures()
            ]

            source_relation_features = layers[RRR_SOURCE_RELATION_TABLE][
                LAYER].getFeatures()

            existing_source_pairs = [
                (source_relation_feature[RRR_SOURCE_SOURCE_FIELD],
                 source_relation_feature[RRR_SOURCE_RESTRICTION_FIELD])
                for source_relation_feature in source_relation_features
            ]
            existing_source_pairs = set(existing_source_pairs)

            rrr_source_relation_pairs = list()

            for administrative_source_id in administrative_source_ids:
                for restriction_feature in new_restriction_features:
                    rrr_source_relation_pair = (
                        administrative_source_id,
                        restriction_feature.attribute(ID_FIELD))
                    rrr_source_relation_pairs.append(rrr_source_relation_pair)

            new_rrr_source_relation_features = list()
            if rrr_source_relation_pairs:
                for id_pair in rrr_source_relation_pairs:
                    if not id_pair in existing_source_pairs:
                        new_feature = QgsVectorLayerUtils().createFeature(
                            layers[RRR_SOURCE_RELATION_TABLE][LAYER])
                        new_feature.setAttribute(RRR_SOURCE_SOURCE_FIELD,
                                                 id_pair[0])
                        new_feature.setAttribute(RRR_SOURCE_RESTRICTION_FIELD,
                                                 id_pair[1])
                        self.log.logMessage(
                            "Saving Restriction-Source: {}-{}".format(
                                id_pair[1], id_pair[0]), PLUGIN_NAME,
                            Qgis.Info)
                        new_rrr_source_relation_features.append(new_feature)

                layers[RRR_SOURCE_RELATION_TABLE][LAYER].dataProvider(
                ).addFeatures(new_rrr_source_relation_features)
                self.qgis_utils.message_emitted.emit(
                    QCoreApplication.translate(
                        "RightOfWay",
                        "{} out of {} records were saved into {}! {} out of {} records already existed in the database."
                    ).format(
                        len(new_rrr_source_relation_features),
                        len(rrr_source_relation_pairs),
                        RRR_SOURCE_RELATION_TABLE,
                        len(rrr_source_relation_pairs) -
                        len(new_rrr_source_relation_features),
                        len(rrr_source_relation_pairs)), Qgis.Info)
Example #24
0
def add_geojson_features(geojson,
                         project,
                         qgis_layer_id=None,
                         connection_id=None,
                         new_layer_name=None,
                         name=None,
                         style=None):
    """Add geojson features to a destination layer, the layer can be specified
    by passing a QgsVectorLayer instance or by specifying a connection and
    a new layer name plus the QDjango project for the new layer.

    The connection may assume the special values `__shapefile__`, `__spatialite__` or `__geopackage__`
    for the creation of new OGR files of the corresponding type.

    The creation of the new layer may raise an exception is a layer with the same name as
    new_layer_name already exists. For already existing layers the `qgis_layer_id` can be used,
    provided that the layer belongs to the current `project`.

    Returns the qgis_layer_id

    :param geojson: new features in GeoJSON format
    :type geojson: str
    :param project: QDjango Project instance for the new or the existing layer
    :type project: Project instance
    :param layer_id: optional, QGIS layer id
    :type layer_id: QGIS layer id
    :param connection: optional, connection id or the special value `__shapefile__`, `__spatialite__` or `__geopackage__`
    :type connection: str
    :param new_layer_name: optional, name of the new layer
    :type new_layer_name: str
    :param name: optional, name of the isochrone, default to current datetime
    :type name: str
    :param style: optional, dictionary with style properties: example {'color': [100, 50, 123], 'transparency': 0.5, 'stroke_width: 3 }
    :type style: dict
    :raises Exception: raise on error
    :rtype: str
    """

    # Additional fields that are not returned by the service as feature attributes
    json_data = json.loads(geojson)

    if name is None:
        name = "Isochrone %s" % QDateTime.currentDateTime().toString(
            Qt.ISODateWithMs)

    metadata = {
        'range_type': json_data['metadata']['query']['range_type'],
        'name': name,
        # 'timestamp': json_data['metadata']['timestamp'],  // Not supported
    }

    for f in json_data['features']:
        f['properties'].update(metadata)

    geojson = json.dumps(json_data)

    fields = QgsJsonUtils.stringToFields(geojson)

    # Patch timestamp type to DateTime // Not supported
    # fields.remove(fields.lookupField('timestamp'))
    #fields.append(QgsField('timestamp', QVariant.DateTime))

    # Create the new layer
    if connection_id is not None:

        def _write_to_ogr(destination_path, new_layer_name, driverName=None):
            """Writes features to new or existing OGR layer"""

            tmp_dir = QTemporaryDir()
            tmp_path = os.path.join(tmp_dir.path(), 'isochrone.json')
            with open(tmp_path, 'w+') as f:
                f.write(geojson)

            tmp_layer = QgsVectorLayer(tmp_path, 'tmp_isochrone', 'ogr')

            if not tmp_layer.isValid():
                raise Exception(
                    _('Cannot create temporary layer for isochrone result.'))

            # Note: shp attribute names are max 10 chars long
            save_options = QgsVectorFileWriter.SaveVectorOptions()
            if driverName is not None:
                save_options.driverName = driverName
            save_options.layerName = new_layer_name
            save_options.fileEncoding = 'utf-8'

            # This is nonsense to me: if the file does not exist the actionOnExistingFile
            # should be ignored instead of raising an error, probable QGIS bug
            if os.path.exists(destination_path):
                # Check if the layer already exists
                layer_exists = QgsVectorFileWriter.targetLayerExists(
                    destination_path, new_layer_name)

                if layer_exists:
                    raise Exception(
                        _('Cannot save isochrone result to destination layer: layer already exists (use "qgis_layer_id" instead)!'
                          ))

                save_options.actionOnExistingFile = QgsVectorFileWriter.CreateOrOverwriteLayer

            error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV2(
                tmp_layer, destination_path,
                project.qgis_project.transformContext(), save_options)

            if error_code != QgsVectorFileWriter.NoError:
                raise Exception(
                    _('Cannot save isochrone result to destination layer: ') +
                    error_message)

            layer_uri = destination_path

            if driverName != 'ESRI Shapefile':
                layer_uri += '|layername=' + new_layer_name

            provider = 'ogr'
            return layer_uri, provider

        destination_path = None

        if connection_id == '__shapefile__':  # new shapefile
            driverName = 'ESRI Shapefile'
            extension = 'shp'
        elif connection_id == '__spatialite__':  # new sqlite
            driverName = 'SpatiaLite'
            extension = 'sqlite'
        elif connection_id == '__geopackage__':  # new gpkg
            driverName = 'GPKG'
            extension = 'gpkg'
        else:  # Add new table to an existing DB connection

            try:
                connection = get_db_connections(
                    project.qgis_project)[connection_id]
            except:
                raise Exception(_('Wrong connection id.'))

            if connection['provider'] == 'ogr':
                destination_path = connection_id
                driverName = 'GPKG' if destination_path.lower().endswith(
                    '.gpkg') else 'SpatiaLite'
            else:
                driverName = None

        # Create a new file/layer
        if driverName is not None:
            new_layer_name = os.path.basename(new_layer_name)

            if destination_path is None:  # new files!
                destination_path = os.path.join(
                    settings.DATASOURCE_PATH,
                    "{}.{}".format(new_layer_name, extension))
                i = 0
                while os.path.exists(destination_path):
                    i += 1
                    destination_path = os.path.join(
                        settings.DATASOURCE_PATH,
                        "{}_{}.{}".format(new_layer_name, i, extension))

            layer_uri, provider = _write_to_ogr(destination_path,
                                                new_layer_name, driverName)

        # Create a new DB table
        else:
            assert connection['provider'] != 'ogr'
            md = QgsProviderRegistry.instance().providerMetadata(
                connection['provider'])
            if not md:
                raise Exception(
                    _('Error creating destination layer connection.'))
            conn = md.createConnection(connection_id, {})
            try:
                conn.createVectorTable(connection['schema'], new_layer_name,
                                       fields, QgsWkbTypes.Polygon,
                                       QgsCoordinateReferenceSystem(4326),
                                       False, {'geometryColumn': 'geom'})
            except QgsProviderConnectionException as ex:
                raise Exception(
                    _('Error creating destination layer: ') + str(ex))

            uri = QgsDataSourceUri(conn.uri())
            uri.setTable(new_layer_name)
            uri.setSchema(connection['schema'])
            uri.setSrid('4326')
            uri.setGeometryColumn('geom')
            provider = connection['provider']
            layer_uri = uri.uri()

        # Now reload the new layer and add it to the project
        qgis_layer = QgsVectorLayer(layer_uri, new_layer_name, provider)
        if not qgis_layer.isValid():
            raise Exception(
                _('Error creating destination layer: layer is not valid!'))

        qgis_layer_id = qgis_layer.id()

        with QgisProjectFileLocker(project) as project:
            apply_style(qgis_layer, style, True, name)
            project.qgis_project.addMapLayers([qgis_layer])
            root = project.qgis_project.layerTreeRoot()
            if qgis_layer_id not in root.findLayerIds():
                # Append layer at the end
                root.insertLayer(-1, qgis_layer)
            if not project.update_qgis_project():
                raise Exception(
                    _('Error saving the destination layer: could not write project!'
                      ))

        # Retrieve the layer again because saving the project deleted it
        qgis_layer = project.qgis_project.mapLayer(qgis_layer_id)

        # Create Layer object
        instance, created = Layer.objects.get_or_create(
            qgs_layer_id=qgis_layer_id,
            project=project,
            defaults={
                'origname':
                new_layer_name,
                'name':
                new_layer_name,
                'title':
                new_layer_name,
                'is_visible':
                True,
                'layer_type':
                provider,
                'srid':
                4326,
                'datasource':
                layer_uri,
                'geometrytype':
                'Polygon',
                # TODO: make this a property of the Layer object
                'database_columns':
                str([{
                    'name': f.name(),
                    'type': QVariant.typeToName(f.type()).upper(),
                    'label': f.displayName()
                } for f in qgis_layer.fields()]),
            })

        if not created:
            raise Exception(
                _('Error adding destination Layer to the project: layer already exists.'
                  ))

        # for OGR (already filled with features) returns the id of the new layer
        if driverName is not None:
            return qgis_layer_id

    # Append to an existing layer
    qgis_layer = project.qgis_project.mapLayer(qgis_layer_id)
    if qgis_layer is None:
        raise Exception(
            _('Error opening destination layer %s: layer not found in QGIS project!'
              % qgis_layer_id))

    features = QgsJsonUtils.stringToFeatureList(geojson, fields)

    compatible_features = []
    for f in features:
        compatible_features.extend(
            QgsVectorLayerUtils.makeFeatureCompatible(f, qgis_layer))

    if qgis_layer.crs().isValid(
    ) and qgis_layer.crs() != QgsCoordinateReferenceSystem(4326):
        ct = QgsCoordinateTransform(QgsCoordinateReferenceSystem(4326),
                                    qgis_layer.crs(), project.qgis_project)
        for f in compatible_features:
            geom = f.geometry()
            if geom.transform(ct) != QgsGeometry.Success:
                raise Exception(
                    _('Error transforming geometry from 4326 to destination layer CRS.'
                      ))
            f.setGeometry(geom)

    if len(compatible_features) == 0:
        raise Exception(_('No compatible features returned from isochrone.'))

    if not qgis_layer.startEditing():
        raise Exception(_('Destination layer is not editable.'))

    # Add features to the layer
    if not qgis_layer.addFeatures(compatible_features):
        raise Exception(_('Error adding features to the destination layer.'))

    if not qgis_layer.commitChanges():
        raise Exception(
            _('Error committing features to the destination layer.'))

    if style is not None:
        with QgisProjectFileLocker(project) as project:
            apply_style(qgis_layer, style, False, name)
            project.update_qgis_project()

    return qgis_layer_id
Example #25
0
def execute_in_place_run(alg,
                         parameters,
                         context=None,
                         feedback=None,
                         raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :type context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :type feedback: QgsProcessingFeedback, optional
    :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback
    :type raise_exceptions: boo, default to False
    :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    # Only feature based algs have sourceFlags
    try:
        if alg.sourceFlags(
        ) & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks:
            context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)
    except AttributeError:
        pass

    active_layer = parameters['INPUT']

    # Run some checks and prepare the layer for in-place execution by:
    # - getting the active layer and checking that it is a vector
    # - making the layer editable if it was not already
    # - selecting all features if none was selected
    # - checking in-place support for the active layer/alg/parameters
    # If one of the check fails and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        if active_layer is None:
            raise QgsProcessingException(tr("There is not active layer."))

        if not isinstance(active_layer, QgsVectorLayer):
            raise QgsProcessingException(
                tr("Active layer is not a vector layer."))

        if not active_layer.isEditable():
            if not active_layer.startEditing():
                raise QgsProcessingException(
                    tr("Active layer is not editable (and editing could not be turned on)."
                       ))

        if not alg.supportInPlaceEdit(active_layer):
            raise QgsProcessingException(
                tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."
                   ))
    except QgsProcessingException as e:
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing',
                                 Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
        return False, {}

    if not active_layer.selectedFeatureIds():
        active_layer.selectAll()

    # Make sure we are working on selected features only
    parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(
        active_layer.id(), True)
    parameters['OUTPUT'] = 'memory:'

    req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
    req.setFlags(QgsFeatureRequest.NoGeometry)
    req.setSubsetOfAttributes([])

    # Start the execution
    # If anything goes wrong and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(
                    tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(
                    tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."
                       ))
            field_idxs = range(len(active_layer.fields()))
            iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds())
            iterator_req.setInvalidGeometryCheck(
                context.invalidGeometryCheck())
            feature_iterator = active_layer.getFeatures(iterator_req)
            step = 100 / len(active_layer.selectedFeatureIds()
                             ) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context,
                                                  feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(
                    new_features, active_layer)

                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(
                            f.id(), dict(zip(field_idxs, new_f.attributes())),
                            dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set(
                        [f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(
                            tr("Error adding processed features back into the layer."
                               ))
                    new_ids = set(
                        [f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle

            # There is no way to know if some features have been skipped
            # due to invalid geometries
            if context.invalidGeometryCheck(
            ) == QgsFeatureRequest.GeometrySkipInvalid:
                selected_ids = active_layer.selectedFeatureIds()
            else:
                selected_ids = []

            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(
                    results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle

                new_features = []

                # Check if there are any skipped features
                if context.invalidGeometryCheck(
                ) == QgsFeatureRequest.GeometrySkipInvalid:
                    missing_ids = list(
                        set(selected_ids) - set(result_layer.allFeatureIds()))
                    if missing_ids:
                        for f in active_layer.getFeatures(
                                QgsFeatureRequest(missing_ids)):
                            if not f.geometry().isGeosValid():
                                new_features.append(f)

                active_layer.deleteFeatures(active_layer.selectedFeatureIds())

                for f in result_layer.getFeatures():
                    new_features.extend(
                        QgsVectorLayerUtils.makeFeaturesCompatible(
                            [f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(
                        tr("Error adding processed features back into the layer."
                           ))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing',
                                 Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)

    return False, {}
    def test_make_features_compatible_attributes(self):
        """Test corner cases for attributes"""

        # Test feature with attributes
        fields = QgsFields()
        fields.append(QgsField('int_f', QVariant.Int))
        fields.append(QgsField('str_f', QVariant.String))
        f1 = QgsFeature(fields)
        f1['int_f'] = 1
        f1['str_f'] = 'str'
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        f2 = f1
        QgsVectorLayerUtils.matchAttributesToFields(f2, fields)
        self.assertEqual(f1.attributes(), f2.attributes())
        self.assertTrue(f1.geometry().asWkt(), f2.geometry().asWkt())

        # Test pad with 0 with fields
        f1.setAttributes([])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], QVariant())
        self.assertEqual(f1.attributes()[1], QVariant())

        # Test pad with 0 without fields
        f1 = QgsFeature()
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], QVariant())
        self.assertEqual(f1.attributes()[1], QVariant())

        # Test drop extra attrs
        f1 = QgsFeature(fields)
        f1.setAttributes([1, 'foo', 'extra'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], 1)
        self.assertEqual(f1.attributes()[1], 'foo')

        # Rearranged fields
        fields2 = QgsFields()
        fields2.append(QgsField('str_f', QVariant.String))
        fields2.append(QgsField('int_f', QVariant.Int))
        f1 = QgsFeature(fields2)
        f1.setAttributes([1, 'foo', 'extra'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)

        # mixed
        fields2.append(QgsField('extra', QVariant.String))
        fields.append(QgsField('extra2', QVariant.Int))
        f1.setFields(fields2)
        f1.setAttributes([1, 'foo', 'blah'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 3)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)
        self.assertEqual(f1.attributes()[2], QVariant())

        fields.append(QgsField('extra', QVariant.Int))
        f1.setAttributes([1, 'foo', 'blah'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 4)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)
        self.assertEqual(f1.attributes()[2], QVariant())
        self.assertEqual(f1.attributes()[3], 'blah')

        # case insensitive
        fields2.append(QgsField('extra3', QVariant.String))
        fields.append(QgsField('EXTRA3', QVariant.Int))
        f1.setFields(fields2)
        f1.setAttributes([1, 'foo', 'blah', 'blergh'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 5)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)
        self.assertEqual(f1.attributes()[2], QVariant())
        self.assertEqual(f1.attributes()[3], 'blah')
        self.assertEqual(f1.attributes()[4], 'blergh')
Example #27
0
    def test_value_exists(self):
        layer = createLayerWithOnePoint()
        # add some more features
        f1 = QgsFeature(2)
        f1.setAttributes(["test1", 124])
        f2 = QgsFeature(3)
        f2.setAttributes(["test2", 125])
        f3 = QgsFeature(4)
        f3.setAttributes(["test3", 126])
        f4 = QgsFeature(5)
        f4.setAttributes(["test4", 127])
        layer.dataProvider().addFeatures([f1, f2, f3, f4])

        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test4'))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 0, 'not present!'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 123))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 124))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 127))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 99))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.valueExists(None, 1, 123))
        # bad field indexes
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, -1, 'test'))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 100, 'test'))

        # with ignore list
        self.assertTrue(
            QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5]))
        self.assertTrue(
            QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [999999]))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [2]))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [99999, 2]))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5, 2]))

        self.assertTrue(
            QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5]))
        self.assertTrue(
            QgsVectorLayerUtils.valueExists(layer, 1, 125, [999999]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [3]))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 1, 125, [99999, 3]))
        self.assertFalse(
            QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5, 3]))
Example #28
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer(
            "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
            "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPointXY(QgsPointXY(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().asWkt(), g.asWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12.0])
        # we expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 12.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueDefinition(2, QgsDefaultValue(None))

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])
Example #29
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer(
            "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
            "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPointXY(QgsPointXY(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().asWkt(), g.asWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # default value takes precedence if it's apply on update
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4', True))
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 12.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        # adjusted so that input value and output feature are the same
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueDefinition(2, QgsDefaultValue(None))

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])

        # test with violated unique constraints and default value expression providing unique value
        layer.setDefaultValueDefinition(1, QgsDefaultValue('130'))
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        self.assertEqual(f.attributes(), ['test_4', 130, NULL])
        # fallback: test with violated unique constraints and default value expression providing already existing value
        # add the feature with the default value:
        self.assertTrue(layer.dataProvider().addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 123
                                              })
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        # and since the default value providing an already existing value (130) it generates a unique value (next int: 131)
        self.assertEqual(f.attributes(), ['test_5', 131, NULL])
        layer.setDefaultValueDefinition(1, QgsDefaultValue(None))

        # test with manually correct unique constraint
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'test_1',
                                                  1: 132
                                              })
        self.assertEqual(f.attributes(), ['test_5', 132, NULL])
Example #30
0
    def check_boundaries_are_not_split(self, db, layer_dict):
        """
        An split boundary is an incomplete boundary because it is connected to
        a single boundary and therefore, they don't represent a change in
        boundary (colindancia).
        """
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Line.BOUNDARIES_ARE_NOT_SPLIT)

        features = []
        boundary_layer = list(layer_dict[QUALITY_RULE_LAYERS].values()
                              )[0] if layer_dict[QUALITY_RULE_LAYERS] else None
        if not boundary_layer:
            return QCoreApplication.translate(
                "LineQualityRules",
                "'Boundary' layer not found!"), Qgis.Critical

        if boundary_layer.featureCount() == 0:
            return (QCoreApplication.translate(
                "LineQualityRules",
                "There are no boundaries to check 'boundaries should not be split'!"
            ), Qgis.Warning)

        else:
            wrong_boundaries = self.geometry.get_boundaries_connected_to_single_boundary(
                db.names, boundary_layer)

            if wrong_boundaries is None:
                return (QCoreApplication.translate(
                    "LineQualityRules",
                    "There are no wrong boundaries!"), Qgis.Success)
            else:
                error_layer = QgsVectorLayer(
                    "LineString?crs={}".format(
                        boundary_layer.sourceCrs().authid()),
                    rule.error_table_name, "memory")
                pr = error_layer.dataProvider()
                pr.addAttributes(rule.error_table_fields)
                error_layer.updateFields()

                # Get geometries from LADM-COL original layer. If not HAS_ADJUSTED_LAYERS, it's OK to get
                # them from wrong_boundaries list, as it would have been created from original geoms
                if layer_dict[HAS_ADJUSTED_LAYERS]:
                    ladm_col_boundary_layer = list(
                        layer_dict[QUALITY_RULE_LADM_COL_LAYERS].values(
                        ))[0] if layer_dict[
                            QUALITY_RULE_LADM_COL_LAYERS] else boundary_layer
                    wrong_tids = ",".join([
                        str(f[db.names.T_ID_F]) for f in wrong_boundaries
                    ])  # Use t_id, which is indexed
                    geoms = {
                        f[db.names.T_ILI_TID_F]: f.geometry()
                        for f in ladm_col_boundary_layer.getFeatures(
                            "{} IN ({})".format(db.names.T_ID_F, wrong_tids))
                    }
                else:
                    geoms = {
                        f[db.names.T_ILI_TID_F]: f.geometry()
                        for f in wrong_boundaries
                    }

                for feature in wrong_boundaries:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        error_layer, geoms[feature[db.names.T_ILI_TID_F]], {
                            0:
                            feature[db.names.T_ILI_TID_F],
                            1:
                            self.quality_rules_manager.get_error_message(
                                QUALITY_RULE_ERROR_CODE_E200201),
                            2:
                            QUALITY_RULE_ERROR_CODE_E200201
                        })
                    features.append(new_feature)

                error_layer.dataProvider().addFeatures(features)
                if error_layer.featureCount() > 0:
                    added_layer = self.app.gui.add_error_layer(db, error_layer)
                    return (QCoreApplication.translate(
                        "LineQualityRules",
                        "A memory layer with {} wrong boundaries has been added to the map!"
                    ).format(added_layer.featureCount()), Qgis.Critical)
                else:
                    return (QCoreApplication.translate(
                        "LineQualityRules",
                        "There are no wrong boundaries."), Qgis.Success)
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPointXY(QgsPointXY(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().asWkt(), g.asWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        #adjusted so that input value and output feature are the same
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueDefinition(2, QgsDefaultValue(None))

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])

        # test with violated unique constraints and default value expression providing unique value
        layer.setDefaultValueDefinition(1, QgsDefaultValue('130'))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        self.assertEqual(f.attributes(), ['test_4', 130, NULL])
        # fallback: test with violated unique constraints and default value expression providing already existing value
        # add the feature with the default value:
        self.assertTrue(layer.dataProvider().addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        # and since the default value providing an already existing value (130) it generates a unique value (next int: 131)
        self.assertEqual(f.attributes(), ['test_5', 131, NULL])
        layer.setDefaultValueDefinition(1, QgsDefaultValue(None))

        # test with manually correct unique constraint
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 132})
        self.assertEqual(f.attributes(), ['test_5', 132, NULL])
Example #32
0
    def post_save(self, features):
        constraint_types_of_parcels = LayerConfig.get_constraint_types_of_parcels(
            self.names)
        message = QCoreApplication.translate(
            "WizardTranslations",
            "'{}' tool has been closed because an error occurred while trying to save the data."
        ).format(self.WIZARD_TOOL_NAME)
        if len(features) != 1:
            message = QCoreApplication.translate(
                "WizardTranslations",
                "'{}' tool has been closed. We should have got only one {} by we have {}"
            ).format(self.WIZARD_TOOL_NAME, self.WIZARD_FEATURE_NAME,
                     len(features))
            self.logger.warning(
                __name__,
                "We should have got only one {}, but we have {}".format(
                    self.WIZARD_FEATURE_NAME, len(features)))
        else:
            fid = features[0].id()

            if not self._layers[self.EDITING_LAYER_NAME].getFeature(
                    fid).isValid():
                self.logger.warning(
                    __name__, "Feature not found in layer {}...".format(
                        self.EDITING_LAYER_NAME))
            else:
                parcel_id = self._layers[self.EDITING_LAYER_NAME].getFeature(
                    fid)[self.names.T_ID_F]

                plot_ids = list()
                building_ids = list()
                building_unit_ids = list()

                # Apply restriction to the selection
                if self.names.LC_PLOT_T in constraint_types_of_parcels[
                        self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]]:
                    if constraint_types_of_parcels[self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]][
                                self.names.LC_PLOT_T] is not None:
                        plot_ids = [
                            f[self.names.T_ID_F] for f in self._layers[
                                self.names.LC_PLOT_T].selectedFeatures()
                        ]
                else:
                    plot_ids = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_PLOT_T].selectedFeatures()
                    ]

                if self.names.LC_BUILDING_T in constraint_types_of_parcels[
                        self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]]:
                    if constraint_types_of_parcels[self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]][
                                self.names.LC_BUILDING_T] is not None:
                        building_ids = [
                            f[self.names.T_ID_F] for f in self._layers[
                                self.names.LC_BUILDING_T].selectedFeatures()
                        ]
                else:
                    building_ids = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_BUILDING_T].selectedFeatures()
                    ]

                if self.names.LC_BUILDING_UNIT_T in constraint_types_of_parcels[
                        self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]]:
                    if constraint_types_of_parcels[self.dict_parcel_type[
                            self.cb_parcel_type.currentText()]][
                                self.names.LC_BUILDING_UNIT_T] is not None:
                        building_unit_ids = [
                            f[self.names.T_ID_F] for f in self._layers[
                                self.names.LC_BUILDING_UNIT_T].
                            selectedFeatures()
                        ]
                else:
                    building_unit_ids = [
                        f[self.names.T_ID_F] for f in self._layers[
                            self.names.LC_BUILDING_UNIT_T].selectedFeatures()
                    ]

                # Fill uebaunit table
                new_features = []
                for plot_id in plot_ids:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        self._layers[self.names.COL_UE_BAUNIT_T])
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_LC_PLOT_F, plot_id)
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_PARCEL_F, parcel_id)
                    self.logger.info(
                        __name__,
                        "Saving Plot-Parcel: {}-{}".format(plot_id, parcel_id))
                    new_features.append(new_feature)

                for building_id in building_ids:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        self._layers[self.names.COL_UE_BAUNIT_T])
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_LC_BUILDING_F, building_id)
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_PARCEL_F, parcel_id)
                    self.logger.info(
                        __name__, "Saving Building-Parcel: {}-{}".format(
                            building_id, parcel_id))
                    new_features.append(new_feature)

                for building_unit_id in building_unit_ids:
                    new_feature = QgsVectorLayerUtils().createFeature(
                        self._layers[self.names.COL_UE_BAUNIT_T])
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_LC_BUILDING_UNIT_F,
                        building_unit_id)
                    new_feature.setAttribute(
                        self.names.COL_UE_BAUNIT_T_PARCEL_F, parcel_id)
                    self.logger.info(
                        __name__, "Saving Building Unit-Parcel: {}-{}".format(
                            building_unit_id, parcel_id))
                    new_features.append(new_feature)

                self._layers[self.names.COL_UE_BAUNIT_T].dataProvider(
                ).addFeatures(new_features)

                if plot_ids and building_ids and building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Plot (t_id={}) and Building(s) (t_id={}) and Building Unit(s) (t_id={})!"
                    ).format(parcel_id, ", ".join([str(b) for b in plot_ids]),
                             ", ".join([str(b) for b in building_ids]),
                             ", ".join([str(b) for b in building_unit_ids]))
                elif plot_ids and building_ids and not building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Plot (t_id={}) and Building(s) (t_id={})!"
                    ).format(parcel_id, ", ".join([str(b) for b in plot_ids]),
                             ", ".join([str(b) for b in building_ids]))
                elif plot_ids and not building_ids and building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Plot (t_id={}) and Building Unit(s) (t_id={})!"
                    ).format(parcel_id, ", ".join([str(b) for b in plot_ids]),
                             ", ".join([str(b) for b in building_unit_ids]))
                elif plot_ids and not building_ids and not building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Plot (t_id={})!"
                    ).format(parcel_id, ", ".join([str(b) for b in plot_ids]))
                elif not plot_ids and building_ids and not building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Building(s) (t_id={})!"
                    ).format(parcel_id,
                             ", ".join([str(b) for b in building_ids]))
                elif not plot_ids and building_ids and building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Building(s) (t_id={}) and Building Unit(s) (t_id={})!"
                    ).format(parcel_id,
                             ", ".join([str(b) for b in building_ids]),
                             ", ".join([str(b) for b in building_unit_ids]))
                elif not plot_ids and not building_ids and building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created and associated with its corresponding Building Unit(s) (t_id={})!"
                    ).format(parcel_id,
                             ", ".join([str(b) for b in building_unit_ids]))
                elif not plot_ids and not building_ids and not building_unit_ids:
                    message = QCoreApplication.translate(
                        "WizardTranslations",
                        "The new parcel (t_id={}) was successfully created but this one wasn't associated with a spatial unit"
                    ).format(parcel_id)

        return message
Example #33
0
def make_features_compatible(new_features, input_layer):
    """Try to make the new features compatible with old features by:

    - converting single to multi part
    - dropping additional attributes
    - adding back M/Z values
    - drop Z/M
    - convert multi part to single part

    :param new_features: new features
    :type new_features: list of QgsFeatures
    :param input_layer: input layer
    :type input_layer: QgsVectorLayer
    :return: modified features
    :rtype: list of QgsFeatures
    """

    input_wkb_type = input_layer.wkbType()
    result_features = []
    for new_f in new_features:
        # Fix attributes
        if new_f.fields().count() > 0:
            attributes = []
            for field in input_layer.fields():
                if new_f.fields().indexFromName(field.name()) >= 0:
                    attributes.append(new_f[field.name()])
                else:
                    attributes.append(None)
            f = QgsFeature(input_layer.fields())
            f.setAttributes(attributes)
            f.setGeometry(new_f.geometry())
            new_f = f
        else:
            lendiff = len(new_f.attributes()) - len(input_layer.fields())
            if lendiff > 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                f.setAttributes(new_f.attributes()[:len(input_layer.fields())])
                new_f = f
            elif lendiff < 0:
                f = QgsFeature(input_layer.fields())
                f.setGeometry(new_f.geometry())
                attributes = new_f.attributes() + [None for i in range(-lendiff)]
                f.setAttributes(attributes)
                new_f = f

        # Check if we need geometry manipulation
        new_f_geom_type = QgsWkbTypes.geometryType(new_f.geometry().wkbType())
        new_f_has_geom = new_f_geom_type not in (QgsWkbTypes.UnknownGeometry, QgsWkbTypes.NullGeometry)
        input_layer_has_geom = input_wkb_type not in (QgsWkbTypes.NoGeometry, QgsWkbTypes.Unknown)

        # Drop geometry if layer is geometry-less
        if not input_layer_has_geom and new_f_has_geom:
            f = QgsFeature(input_layer.fields())
            f.setAttributes(new_f.attributes())
            new_f = f
            result_features.append(new_f)
            continue  # skip the rest

        if input_layer_has_geom and new_f_has_geom and \
                new_f.geometry().wkbType() != input_wkb_type:  # Fix geometry
            # Single -> Multi
            if (QgsWkbTypes.isMultiType(input_wkb_type) and not
                    new_f.geometry().isMultipart()):
                new_geom = new_f.geometry()
                new_geom.convertToMultiType()
                new_f.setGeometry(new_geom)
            # Drop Z/M
            if (new_f.geometry().constGet().is3D() and not QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropZValue()
                new_f.setGeometry(new_geom)
            if (new_f.geometry().constGet().isMeasure() and not QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropMValue()
                new_f.setGeometry(new_geom)
            # Add Z/M back (set it to 0)
            if (not new_f.geometry().constGet().is3D() and QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addZValue(0.0)
                new_f.setGeometry(new_geom)
            if (not new_f.geometry().constGet().isMeasure() and QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addMValue(0.0)
                new_f.setGeometry(new_geom)
            # Multi -> Single
            if (not QgsWkbTypes.isMultiType(input_wkb_type) and
                    new_f.geometry().isMultipart()):
                g = new_f.geometry()
                g2 = g.constGet()
                for i in range(g2.partCount()):
                    # Clone or crash!
                    g4 = QgsGeometry(g2.geometryN(i).clone())
                    f = QgsVectorLayerUtils.createFeature(input_layer, g4, {i: new_f.attribute(i) for i in range(new_f.fields().count())})
                    result_features.append(f)
            else:
                result_features.append(new_f)
        else:
            result_features.append(new_f)
    return result_features
Example #34
0
    def testDuplicateFeature(self):
        """ test duplicating a feature """

        project = QgsProject().instance()

        # LAYERS
        # - add first layer (parent)
        layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer",
                                "parentlayer", "memory")
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # -  set the value for the copy
        layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)"))
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # - add second layer (child)
        layer2 = QgsVectorLayer("Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer",
                                "childlayer", "memory")
        # > check second layer (child)
        self.assertTrue(layer2.isValid())
        # - add layers
        project.addMapLayers([layer1, layer2])

        # FEATURES
        # - add 2 features on layer1 (parent)
        l1f1orig = QgsFeature()
        l1f1orig.setFields(layer1.fields())
        l1f1orig.setAttributes(["F_l1f1", 100])
        l1f2orig = QgsFeature()
        l1f2orig.setFields(layer1.fields())
        l1f2orig.setAttributes(["F_l1f2", 101])
        # > check by adding features
        self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig, l1f2orig]))
        # add 4 features on layer2 (child)
        l2f1orig = QgsFeature()
        l2f1orig.setFields(layer2.fields())
        l2f1orig.setAttributes(["F_l2f1", 201, 100])
        l2f2orig = QgsFeature()
        l2f2orig.setFields(layer2.fields())
        l2f2orig.setAttributes(["F_l2f2", 202, 100])
        l2f3orig = QgsFeature()
        l2f3orig.setFields(layer2.fields())
        l2f3orig.setAttributes(["F_l2f3", 203, 100])
        l2f4orig = QgsFeature()
        l2f4orig.setFields(layer2.fields())
        l2f4orig.setAttributes(["F_l2f4", 204, 101])
        # > check by adding features
        self.assertTrue(layer2.dataProvider().addFeatures([l2f1orig, l2f2orig, l2f3orig, l2f4orig]))

        # RELATION
        # - create the relationmanager
        relMgr = project.relationManager()
        # - create the relation
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('childrel')
        rel.setReferencingLayer(layer2.id())
        rel.setReferencedLayer(layer1.id())
        rel.addFieldPair('foreign_key', 'pkid')
        rel.setStrength(QgsRelation.Composition)
        # > check relation
        self.assertTrue(rel.isValid())
        # - add relation
        relMgr.addRelation(rel)
        # > check if referencedLayer is layer1
        self.assertEqual(rel.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(rel.referencingLayer(), layer2)
        # > check if the layers are correct in relation when loading from relationManager
        relations = project.relationManager().relations()
        relation = relations[list(relations.keys())[0]]
        # > check if referencedLayer is layer1
        self.assertEqual(relation.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(relation.referencingLayer(), layer2)
        # > check the relatedfeatures

        '''
        # testoutput 1
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        print( "\n--------------------------")

        print( "\nFeatures on layer1")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2")
        for f in layer2.getFeatures():
            print( f.attributes() )
        '''

        # DUPLICATION
        # - duplicate feature l1f1orig with children
        layer1.startEditing()
        results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig, project, 0)

        # > check if name is name of duplicated (pk is different)
        result_feature = results[0]
        self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))
        # > check duplicated child layer
        result_layer = results[1].layers()[0]
        self.assertEqual(result_layer, layer2)
        #  > check duplicated child features
        self.assertTrue(results[1].duplicatedFeatures(result_layer))

        '''
        # testoutput 2
        print( "\nFeatures on layer1 (after duplication)")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2 (after duplication)")
        for f in layer2.getFeatures():
            print( f.attributes() )
            
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        '''

        # > compare text of parent feature
        self.assertEqual(result_feature.attribute('fldtxt'), l1f1orig.attribute('fldtxt'))

        # - create copyValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(result_feature)
        copyValueList = []
        while relfeatit.nextFeature(childFeature):
            copyValueList.append(childFeature.attribute('fldtxt'))
        # - create origValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(l1f1orig)
        origValueList = []
        while relfeatit.nextFeature(childFeature):
            origValueList.append(childFeature.attribute('fldtxt'))

        # - check if the ids are still the same
        self.assertEqual(copyValueList, origValueList)
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid())

        # basic tests
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertTrue(f.isValid())
        self.assertEqual(f.fields(), layer.fields())
        self.assertFalse(f.hasGeometry())
        self.assertEqual(f.attributes(), [NULL, NULL, NULL])

        # set geometry
        g = QgsGeometry.fromPointXY(QgsPointXY(100, 200))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        self.assertTrue(f.hasGeometry())
        self.assertEqual(f.geometry().asWkt(), g.asWkt())

        # using attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # layer with default value expression based on geometry
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x'))
        f = QgsVectorLayerUtils.createFeature(layer, g)
        #adjusted so that input value and output feature are the same
        self.assertEqual(f.attributes(), [NULL, NULL, 300.0])
        layer.setDefaultValueDefinition(2, QgsDefaultValue(None))

        # test with violated unique constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128
        self.assertEqual(f.attributes(), ['test_1', 128, NULL])
        layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique)
        # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        self.assertEqual(f.attributes(), ['test_4', 128, NULL])

        # test with violated unique constraints and default value expression providing unique value
        layer.setDefaultValueDefinition(1, QgsDefaultValue('130'))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        self.assertEqual(f.attributes(), ['test_4', 130, NULL])
        # fallback: test with violated unique constraints and default value expression providing already existing value
        # add the feature with the default value:
        self.assertTrue(layer.dataProvider().addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123})
        # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value
        # and since the default value providing an already existing value (130) it generates a unique value (next int: 131)
        self.assertEqual(f.attributes(), ['test_5', 131, NULL])
        layer.setDefaultValueDefinition(1, QgsDefaultValue(None))

        # test with manually correct unique constraint
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 132})
        self.assertEqual(f.attributes(), ['test_5', 132, NULL])

        """ test creating a feature respecting unique values of postgres provider """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")

        # init connection string
        dbconn = 'dbname=\'qgis_test\''
        if 'QGIS_PGTEST_DB' in os.environ:
            dbconn = os.environ['QGIS_PGTEST_DB']

        # create a vector layer
        pg_layer = QgsVectorLayer('{} table="qgis_test"."authors" sql='.format(dbconn), "authors", "postgres")
        self.assertTrue(pg_layer.isValid())
        # check the default clause
        default_clause = 'nextval(\'qgis_test.authors_pk_seq\'::regclass)'
        self.assertEqual(pg_layer.dataProvider().defaultValueClause(0), default_clause)

        # though default_clause is after the first create not unique (until save), it should fill up all the features with it
        pg_layer.startEditing()
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        self.assertTrue(QgsVectorLayerUtils.valueExists(pg_layer, 0, default_clause))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [default_clause, NULL])
        self.assertTrue(pg_layer.addFeatures([f]))
        # if a unique value is passed, use it
        f = QgsVectorLayerUtils.createFeature(pg_layer, attributes={0: 40, 1: NULL})
        self.assertEqual(f.attributes(), [40, NULL])
        # and if a default value is configured use it as well
        pg_layer.setDefaultValueDefinition(0, QgsDefaultValue('11*4'))
        f = QgsVectorLayerUtils.createFeature(pg_layer)
        self.assertEqual(f.attributes(), [44, NULL])
        pg_layer.rollBack()
Example #36
0
    def check_uebaunit_parcel(self, db):
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Logic.UEBAUNIT_PARCEL)
        error_layer = QgsVectorLayer("NoGeometry", rule.error_table_name,
                                     "memory")
        pr = error_layer.dataProvider()
        pr.addAttributes(rule.error_table_fields)
        error_layer.updateFields()

        res, records = self.get_ladm_queries(db.engine).get_uebaunit_parcel(db)

        new_features = list()
        if res:
            for record in records:
                plot_count = record[
                    'sum_t']  # count of plots associated to the parcel
                building_count = record[
                    'sum_c']  # count of buildings associated to the parcel
                building_unit_count = record[
                    'sum_uc']  # count of building units associated to the parcel

                if record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_NO_HORIZONTAL_PROPERTY:
                    error_code = QUALITY_RULE_ERROR_CODE_E401011
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_PARENT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401008
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_CONDOMINIUM_PARENT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401002
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_CEMETERY_PARENT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401006
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_PUBLIC_USE:
                    error_code = QUALITY_RULE_ERROR_CODE_E401001
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_CONDOMINIUM_PARCEL_UNIT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401003
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_ROAD:
                    error_code = QUALITY_RULE_ERROR_CODE_E401010
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_CEMETERY_PARCEL_UNIT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401007
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_PARCEL_UNIT:
                    error_code = QUALITY_RULE_ERROR_CODE_E401009
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_HORIZONTAL_PROPERTY_MEJORA:
                    error_code = QUALITY_RULE_ERROR_CODE_E401005
                elif record[
                        db.names.
                        LC_PARCEL_T_PARCEL_TYPE_F] == LADMNames.PARCEL_TYPE_NO_HORIZONTAL_PROPERTY_MEJORA:
                    error_code = QUALITY_RULE_ERROR_CODE_E401004

                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, QgsGeometry(), {
                        0: record[db.names.T_ILI_TID_F],
                        1: plot_count,
                        2: building_count,
                        3: building_unit_count,
                        4: self.quality_rules_manager.get_error_message(
                            error_code),
                        5: error_code
                    })
                new_features.append(new_feature)

            error_layer.dataProvider().addFeatures(new_features)
        else:
            self.logger.error_msg(
                __name__, "Error executing query for rule {}: {}".format(
                    rule.rule_name, records))

        return self.return_message(db, rule.rule_name, error_layer)
Example #37
0
    def testDuplicateFeature(self):
        """ test duplicating a feature """

        project = QgsProject().instance()

        # LAYERS
        # - add first layer (parent)
        layer1 = QgsVectorLayer("Point?field=fldtxt:string&field=pkid:integer",
                                "parentlayer", "memory")
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # -  set the value for the copy
        layer1.setDefaultValueDefinition(1, QgsDefaultValue("rand(1000,2000)"))
        # > check first layer (parent)
        self.assertTrue(layer1.isValid())
        # - add second layer (child)
        layer2 = QgsVectorLayer(
            "Point?field=fldtxt:string&field=id:integer&field=foreign_key:integer",
            "childlayer", "memory")
        # > check second layer (child)
        self.assertTrue(layer2.isValid())
        # - add layers
        project.addMapLayers([layer1, layer2])

        # FEATURES
        # - add 2 features on layer1 (parent)
        l1f1orig = QgsFeature()
        l1f1orig.setFields(layer1.fields())
        l1f1orig.setAttributes(["F_l1f1", 100])
        l1f2orig = QgsFeature()
        l1f2orig.setFields(layer1.fields())
        l1f2orig.setAttributes(["F_l1f2", 101])
        # > check by adding features
        self.assertTrue(layer1.dataProvider().addFeatures([l1f1orig,
                                                           l1f2orig]))
        # add 4 features on layer2 (child)
        l2f1orig = QgsFeature()
        l2f1orig.setFields(layer2.fields())
        l2f1orig.setAttributes(["F_l2f1", 201, 100])
        l2f2orig = QgsFeature()
        l2f2orig.setFields(layer2.fields())
        l2f2orig.setAttributes(["F_l2f2", 202, 100])
        l2f3orig = QgsFeature()
        l2f3orig.setFields(layer2.fields())
        l2f3orig.setAttributes(["F_l2f3", 203, 100])
        l2f4orig = QgsFeature()
        l2f4orig.setFields(layer2.fields())
        l2f4orig.setAttributes(["F_l2f4", 204, 101])
        # > check by adding features
        self.assertTrue(layer2.dataProvider().addFeatures(
            [l2f1orig, l2f2orig, l2f3orig, l2f4orig]))

        # RELATION
        # - create the relationmanager
        relMgr = project.relationManager()
        # - create the relation
        rel = QgsRelation()
        rel.setId('rel1')
        rel.setName('childrel')
        rel.setReferencingLayer(layer2.id())
        rel.setReferencedLayer(layer1.id())
        rel.addFieldPair('foreign_key', 'pkid')
        rel.setStrength(QgsRelation.Composition)
        # > check relation
        self.assertTrue(rel.isValid())
        # - add relation
        relMgr.addRelation(rel)
        # > check if referencedLayer is layer1
        self.assertEqual(rel.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(rel.referencingLayer(), layer2)
        # > check if the layers are correct in relation when loading from relationManager
        relations = project.relationManager().relations()
        relation = relations[list(relations.keys())[0]]
        # > check if referencedLayer is layer1
        self.assertEqual(relation.referencedLayer(), layer1)
        # > check if referencingLayer is layer2
        self.assertEqual(relation.referencingLayer(), layer2)
        # > check the relatedfeatures
        '''
        # testoutput 1
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        print( "\n--------------------------")

        print( "\nFeatures on layer1")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2")
        for f in layer2.getFeatures():
            print( f.attributes() )
        '''

        # DUPLICATION
        # - duplicate feature l1f1orig with children
        layer1.startEditing()
        results = QgsVectorLayerUtils.duplicateFeature(layer1, l1f1orig,
                                                       project, 0)

        # > check if name is name of duplicated (pk is different)
        result_feature = results[0]
        self.assertEqual(result_feature.attribute('fldtxt'),
                         l1f1orig.attribute('fldtxt'))
        # > check duplicated child layer
        result_layer = results[1].layers()[0]
        self.assertEqual(result_layer, layer2)
        #  > check duplicated child features
        self.assertTrue(results[1].duplicatedFeatures(result_layer))
        '''
        # testoutput 2
        print( "\nFeatures on layer1 (after duplication)")
        for f in layer1.getFeatures():
            print( f.attributes() )

        print( "\nFeatures on layer2 (after duplication)")
        for f in layer2.getFeatures():
            print( f.attributes() )
            
        print( "\nAll Features and relations")
        featit=layer1.getFeatures()
        f=QgsFeature()
        while featit.nextFeature(f):
            print( f.attributes())
            childFeature = QgsFeature()
            relfeatit=rel.getRelatedFeatures(f)
            while relfeatit.nextFeature(childFeature):
                 print( childFeature.attributes() )
        '''

        # > compare text of parent feature
        self.assertEqual(result_feature.attribute('fldtxt'),
                         l1f1orig.attribute('fldtxt'))

        # - create copyValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(result_feature)
        copyValueList = []
        while relfeatit.nextFeature(childFeature):
            copyValueList.append(childFeature.attribute('fldtxt'))
        # - create origValueList
        childFeature = QgsFeature()
        relfeatit = rel.getRelatedFeatures(l1f1orig)
        origValueList = []
        while relfeatit.nextFeature(childFeature):
            origValueList.append(childFeature.attribute('fldtxt'))

        # - check if the ids are still the same
        self.assertEqual(copyValueList, origValueList)
Example #38
0
    def get_boundary_features_not_covered_by_plots(self, db, plot_layer,
                                                   boundary_layer,
                                                   more_bfs_layer, less_layer,
                                                   error_layer, id_field):
        """
        Return all boundary features that have errors when checking if they are covered by plots.
        This takes into account both geometric and alphanumeric (topology table) errors.
        """
        dict_uuid_plot = get_uuid_dict(plot_layer, db.names, db.names.T_ID_F)
        dict_uuid_boundary = get_uuid_dict(boundary_layer, db.names,
                                           db.names.T_ID_F)
        plot_as_lines_layer = processing.run("ladm_col:polygonstolines", {
            'INPUT': plot_layer,
            'OUTPUT': 'memory:'
        })['OUTPUT']

        # create dict with layer data
        id_field_idx = plot_as_lines_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_plot_as_lines = {
            feature[id_field]: feature
            for feature in plot_as_lines_layer.getFeatures(request)
        }

        id_field_idx = boundary_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_boundary = {
            feature[id_field]: feature
            for feature in boundary_layer.getFeatures(request)
        }

        exp_more = '"{}" is not null and "{}" is not null'.format(
            db.names.MORE_BFS_T_LC_BOUNDARY_F, db.names.MORE_BFS_T_LC_PLOT_F)
        list_more_bfs = [{
            'plot_id':
            feature[db.names.MORE_BFS_T_LC_PLOT_F],
            'boundary_id':
            feature[db.names.MORE_BFS_T_LC_BOUNDARY_F]
        } for feature in more_bfs_layer.getFeatures(exp_more)]

        exp_less = '"{}" is not null and "{}" is not null'.format(
            db.names.LESS_BFS_T_LC_BOUNDARY_F, db.names.LESS_BFS_T_LC_PLOT_F)
        list_less = [{
            'plot_id': feature[db.names.LESS_BFS_T_LC_PLOT_F],
            'boundary_id': feature[db.names.LESS_BFS_T_LC_BOUNDARY_F]
        } for feature in less_layer.getFeatures(exp_less)]

        tmp_inner_rings_layer = self.geometry.get_inner_rings_layer(
            db.names, plot_layer, db.names.T_ID_F)
        inner_rings_layer = processing.run(
            "native:addautoincrementalfield", {
                'INPUT': tmp_inner_rings_layer,
                'FIELD_NAME': 'AUTO',
                'START': 0,
                'GROUP_FIELDS': [],
                'SORT_EXPRESSION': '',
                'SORT_ASCENDING': True,
                'SORT_NULLS_FIRST': False,
                'OUTPUT': 'memory:'
            })['OUTPUT']

        id_field_idx = inner_rings_layer.fields().indexFromName(id_field)
        auto_idx = inner_rings_layer.fields().indexFromName('AUTO')
        request = QgsFeatureRequest().setSubsetOfAttributes(
            [id_field_idx, auto_idx])
        dict_inner_rings = {
            '{}-{}'.format(feature[id_field], feature['AUTO']): feature
            for feature in inner_rings_layer.getFeatures(request)
        }

        # spatial joins between boundary and inner rings
        # we use as input layer the rings because it is the existing information
        # in the terrain polygons and filters better because they are less records
        spatial_join_inner_rings_boundary_layer = processing.run(
            "qgis:joinattributesbylocation",
            {
                'INPUT': boundary_layer,
                'JOIN': inner_rings_layer,
                'PREDICATE': [0],  # Intersects
                'JOIN_FIELDS': [id_field, 'AUTO'],
                'METHOD': 0,
                'DISCARD_NONMATCHING': True,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        list_spatial_join_boundary_inner_rings = list()
        list_spatial_join_boundary_plot_ring = list()
        for feature in spatial_join_inner_rings_boundary_layer.getFeatures():
            # The id field has the same name for both layers
            # This list is only used to check plot's inner rings without boundaries
            list_spatial_join_boundary_inner_rings.append({
                'plot_ring_id':
                '{}-{}'.format(feature[id_field + '_2'], feature['AUTO']),
                'boundary_id':
                feature[id_field]
            })

            # list create for filter inner rings from spatial join with between plot and boundary
            list_spatial_join_boundary_plot_ring.append({
                'plot_id':
                feature[id_field + '_2'],
                'boundary_id':
                feature[id_field]
            })

        # Spatial join between boundary and plots as lines
        spatial_join_boundary_plot_layer = processing.run(
            "qgis:joinattributesbylocation", {
                'INPUT': boundary_layer,
                'JOIN': plot_as_lines_layer,
                'PREDICATE': [0],
                'JOIN_FIELDS': [id_field],
                'METHOD': 0,
                'DISCARD_NONMATCHING': True,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        # The id field has the same name for both layers
        list_spatial_join_boundary_plot = [{
            'plot_id': feature[id_field + '_2'],
            'boundary_id': feature[id_field]
        } for feature in spatial_join_boundary_plot_layer.getFeatures()]

        #####################################################
        # Validation of geometric errors
        #####################################################

        # Identify plots with geometry problems and remove coincidence in spatial join between plot as line and boundary
        # and inner_rings and boundary. If the geometry fails, there is no need to check further topological rules for
        # plots

        errors_boundary_plot_diffs = self.geometry.difference_boundary_plot(
            db.names, boundary_layer, plot_as_lines_layer, db.names.T_ID_F)
        for error_diff in errors_boundary_plot_diffs:
            boundary_id = error_diff['id']
            # All boundaries with geometric errors are eliminated. It is not necessary check more
            # in spatial join between boundary and plot as line
            for item_sj in list_spatial_join_boundary_plot.copy():
                if item_sj['boundary_id'] == boundary_id:
                    list_spatial_join_boundary_plot.remove(item_sj)

            # All boundaries with geometric errors are eliminated. It is not necessary check more
            # in spatial join between boundary and inner_rings
            for item_sj in list_spatial_join_boundary_inner_rings.copy():
                if item_sj['boundary_id'] == boundary_id:
                    list_spatial_join_boundary_inner_rings.remove(item_sj)

        ######################################################
        # Validation of errors in alphanumeric topology tables
        ######################################################

        # start validation in alphanumeric topology tables for more_bfs
        # remove spatial join intersection with geometries that no contain lines. Because it is not necessary to check
        for item_sj in list_spatial_join_boundary_plot.copy():
            boundary_id = item_sj['boundary_id']
            plot_id = item_sj['plot_id']

            if item_sj in list_spatial_join_boundary_plot_ring:
                # it is removed because it is registered in the spatial join between rings and boundaries
                # and it shouldn't be registered in the topology table of more_bfs
                list_spatial_join_boundary_plot.remove(item_sj)
            else:
                boundary_geom = dict_boundary[boundary_id].geometry()
                plot_geom = dict_plot_as_lines[plot_id].geometry()
                intersection = boundary_geom.intersection(plot_geom)

                if not intersection.isEmpty():
                    if intersection.type() != QgsWkbTypes.LineGeometry:
                        if intersection.type() == QgsWkbTypes.UnknownGeometry:
                            has_line = False
                            for part in intersection.asGeometryCollection():
                                if part.isMultipart():
                                    for i in range(part.numGeometries()):
                                        if QgsWkbTypes.geometryType(
                                                part.geometryN(i).wkbType()
                                        ) == QgsWkbTypes.LineGeometry:
                                            has_line = True
                                            break
                                else:
                                    if part.type() == QgsWkbTypes.LineGeometry:
                                        has_line = True
                                        break
                            if not has_line:
                                # Remove point intersections plot-boundary
                                list_spatial_join_boundary_plot.remove(item_sj)
                        else:
                            list_spatial_join_boundary_plot.remove(item_sj)

        # Check relation between plot and boundary not registered in more_bfs
        errors_not_in_more_bfs = list()
        errors_duplicate_in_more_bfs = list()
        for item_sj_bp in list_spatial_join_boundary_plot:
            count_more_bfs = list_more_bfs.count(item_sj_bp)
            if count_more_bfs > 1:
                errors_duplicate_in_more_bfs.append(
                    (item_sj_bp['plot_id'], item_sj_bp['boundary_id']))
            elif count_more_bfs == 0:
                # Check for the special case of two contiguous plots, one of them covers the common boundary, but the
                # other one does not! This should be still a geometry error but is not captured by the code above. Only
                # in this point of the whole checks we can validate between the individual boundary and the individual
                # plot.
                boundary_geom = dict_boundary[
                    item_sj_bp['boundary_id']].geometry()
                plot_geom = dict_plot_as_lines[
                    item_sj_bp['plot_id']].geometry()
                intersection = boundary_geom.intersection(plot_geom)

                if not intersection.isEmpty():
                    if intersection.isGeosEqual(boundary_geom):
                        errors_not_in_more_bfs.append(
                            (item_sj_bp['plot_id'], item_sj_bp['boundary_id']))
                    else:
                        errors_boundary_plot_diffs.append({
                            'id':
                            item_sj_bp['boundary_id'],
                            'id_plot':
                            item_sj_bp['plot_id'],
                            'geometry':
                            boundary_geom
                        })

        # finalize validation in more_bfs table

        # start validation in less table
        errors_not_in_less = list()
        errors_duplicate_in_less = list()
        # start validation in alphanumeric topology tables for less
        # remove spatial join intersection with geometries that do not contain lines.
        # Because it is not necessary to check topology register
        for inner_ring in list_spatial_join_boundary_inner_rings:
            boundary_id = inner_ring['boundary_id']
            plot_ring_id = inner_ring['plot_ring_id']

            boundary_geom = dict_boundary[boundary_id].geometry()
            inner_ring_geom = dict_inner_rings[plot_ring_id].geometry()

            # check intersections difference to line, we check that collections do not have lines parts
            intersection = boundary_geom.intersection(inner_ring_geom)
            has_line = False

            if not intersection.isEmpty():
                if intersection.type() != QgsWkbTypes.LineGeometry:
                    if intersection.type() == QgsWkbTypes.UnknownGeometry:
                        for part in intersection.asGeometryCollection():
                            if part.isMultipart():
                                for i in range(part.numGeometries()):
                                    if QgsWkbTypes.geometryType(
                                            part.geometryN(i).wkbType()
                                    ) == QgsWkbTypes.LineGeometry:
                                        has_line = True
                                        break
                            else:
                                if part.type() == QgsWkbTypes.LineGeometry:
                                    has_line = True
                                    break
                else:
                    has_line = True

            if has_line:
                tmp_dict_plot_boundary = {
                    'plot_id': int(plot_ring_id.split('-')[0]),
                    'boundary_id': boundary_id
                }
                count_less = list_less.count(tmp_dict_plot_boundary)

                if count_less > 1:
                    errors_duplicate_in_less.append(
                        (plot_ring_id, boundary_id))  # duplicate in less table
                elif count_less == 0:
                    errors_not_in_less.append(
                        (plot_ring_id,
                         boundary_id))  # no registered less table
        # finalize validation for less table

        features = list()

        # boundary not covered by plot
        for boundary_plot_diff in errors_boundary_plot_diffs:
            boundary_id = boundary_plot_diff['id']
            boundary_geom = boundary_plot_diff['geometry']
            plot_id = boundary_plot_diff[
                'id_plot'] if 'id_plot' in boundary_plot_diff else None
            new_feature = QgsVectorLayerUtils().createFeature(
                error_layer, boundary_geom, {
                    0:
                    dict_uuid_boundary.get(boundary_id),
                    1:
                    dict_uuid_plot.get(plot_id),
                    2:
                    self.quality_rules_manager.get_error_message(
                        QUALITY_RULE_ERROR_CODE_E200301),
                    3:
                    QUALITY_RULE_ERROR_CODE_E200301
                })
            features.append(new_feature)

        # No registered more bfs
        if errors_not_in_more_bfs:
            for error_more_bfs in set(errors_not_in_more_bfs):
                plot_id = error_more_bfs[0]  # plot_id
                boundary_id = error_more_bfs[1]  # boundary_id
                geom_boundary = dict_boundary[boundary_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_boundary, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200304),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200304
                    })
                features.append(new_feature)

        # Duplicate in more bfs
        if errors_duplicate_in_more_bfs:
            for error_more_bfs in set(errors_duplicate_in_more_bfs):
                plot_id = error_more_bfs[0]  # plot_id
                boundary_id = error_more_bfs[1]  # boundary_id
                geom_boundary = dict_boundary[boundary_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_boundary, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200302),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200302
                    })
                features.append(new_feature)

        # No registered less
        if errors_not_in_less:
            for error_less in set(errors_not_in_less):
                plot_ring_id = error_less[0]  # plot_ring_id
                plot_id = int(plot_ring_id.split('-')[0])  # plot_id
                boundary_id = error_less[1]  # boundary_id
                geom_ring = dict_inner_rings[plot_ring_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_ring, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200305),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200305
                    })
                features.append(new_feature)

        # Duplicate in less
        if errors_duplicate_in_less:
            for error_less in set(errors_duplicate_in_less):
                plot_ring_id = error_less[0]  # plot_ring_id
                plot_id = int(plot_ring_id.split('-')[0])  # plot_id
                boundary_id = error_less[1]  # boundary_id
                geom_ring = dict_inner_rings[plot_ring_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, geom_ring, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_plot.get(plot_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200303),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200303
                    })
                features.append(new_feature)

        return features
    def test_make_features_compatible_attributes(self):
        """Test corner cases for attributes"""

        # Test feature with attributes
        fields = QgsFields()
        fields.append(QgsField('int_f', QVariant.Int))
        fields.append(QgsField('str_f', QVariant.String))
        f1 = QgsFeature(fields)
        f1['int_f'] = 1
        f1['str_f'] = 'str'
        f1.setGeometry(QgsGeometry.fromWkt('Point(9 45)'))
        f2 = f1
        QgsVectorLayerUtils.matchAttributesToFields(f2, fields)
        self.assertEqual(f1.attributes(), f2.attributes())
        self.assertTrue(f1.geometry().asWkt(), f2.geometry().asWkt())

        # Test pad with 0 with fields
        f1.setAttributes([])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], QVariant())
        self.assertEqual(f1.attributes()[1], QVariant())

        # Test pad with 0 without fields
        f1 = QgsFeature()
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], QVariant())
        self.assertEqual(f1.attributes()[1], QVariant())

        # Test drop extra attrs
        f1 = QgsFeature(fields)
        f1.setAttributes([1, 'foo', 'extra'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], 1)
        self.assertEqual(f1.attributes()[1], 'foo')

        # Rearranged fields
        fields2 = QgsFields()
        fields2.append(QgsField('str_f', QVariant.String))
        fields2.append(QgsField('int_f', QVariant.Int))
        f1 = QgsFeature(fields2)
        f1.setAttributes([1, 'foo', 'extra'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 2)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)

        # mixed
        fields2.append(QgsField('extra', QVariant.String))
        fields.append(QgsField('extra2', QVariant.Int))
        f1.setFields(fields2)
        f1.setAttributes([1, 'foo', 'blah'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 3)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)
        self.assertEqual(f1.attributes()[2], QVariant())

        fields.append(QgsField('extra', QVariant.Int))
        f1.setAttributes([1, 'foo', 'blah'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 4)
        self.assertEqual(f1.attributes()[0], 1)
        self.assertEqual(f1.attributes()[1], 'foo')
        self.assertEqual(f1.attributes()[2], 'blah')
        self.assertEqual(f1.attributes()[3], QVariant())

        # case insensitive
        fields2.append(QgsField('extra3', QVariant.String))
        fields.append(QgsField('EXTRA3', QVariant.Int))
        f1.setFields(fields2)
        f1.setAttributes([1, 'foo', 'blah', 'blergh'])
        QgsVectorLayerUtils.matchAttributesToFields(f1, fields)
        self.assertEqual(len(f1.attributes()), 5)
        self.assertEqual(f1.attributes()[0], 'foo')
        self.assertEqual(f1.attributes()[1], 1)
        self.assertEqual(f1.attributes()[2], QVariant())
        self.assertEqual(f1.attributes()[3], 'blah')
        self.assertEqual(f1.attributes()[4], 'blergh')
Example #40
0
    def check_boundary_points_covered_by_plot_nodes(self, db, layer_dict):
        rule = self.quality_rules_manager.get_quality_rule(
            EnumQualityRule.Point.BOUNDARY_POINTS_COVERED_BY_PLOT_NODES)

        layers = layer_dict[QUALITY_RULE_LAYERS]

        if not layers:
            return QCoreApplication.translate(
                "PointQualityRules",
                "At least one required layer (plot, boundary point) was not found!"
            ), Qgis.Critical

        if layers[db.names.LC_BOUNDARY_POINT_T].featureCount() == 0:
            return (QCoreApplication.translate(
                "PointQualityRules",
                "There are no boundary points to check 'boundary points should be covered by Plot nodes'."
            ), Qgis.Warning)

        else:
            error_layer = QgsVectorLayer(
                "Point?crs={}".format(
                    layers[db.names.LC_BOUNDARY_POINT_T].sourceCrs().authid()),
                rule.error_table_name, "memory")

            data_provider = error_layer.dataProvider()
            data_provider.addAttributes(rule.error_table_fields)
            error_layer.updateFields()

            point_list = self.get_boundary_points_features_not_covered_by_plot_nodes(
                layers[db.names.LC_BOUNDARY_POINT_T],
                layers[db.names.LC_PLOT_T], db.names.T_ILI_TID_F)
            features = list()
            for point in point_list:
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer,
                    point[1],  # Geometry
                    {
                        0:
                        point[0],  # feature uuid
                        1:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100401),
                        2:
                        QUALITY_RULE_ERROR_CODE_E100401
                    })
                features.append(new_feature)

            error_layer.dataProvider().addFeatures(features)

            if error_layer.featureCount() > 0:
                added_layer = self.app.gui.add_error_layer(db, error_layer)

                return (QCoreApplication.translate(
                    "PointQualityRules",
                    "A memory layer with {} boundary points not covered by plot nodes has been added to the map!"
                ).format(added_layer.featureCount()), Qgis.Critical)

            else:
                return (QCoreApplication.translate(
                    "PointQualityRules",
                    "All boundary points are covered by plot nodes!"),
                        Qgis.Success)
Example #41
0
    def processAlgorithm(self, parameters, context, feedback):
        source = self.parameterAsSource(parameters, self.INPUT, context)
        source_fields_parameter = self.parameterAsFields(parameters, self.INPUT_FIELD, context)
        target = self.parameterAsVectorLayer(parameters, self.OUTPUT, context)
        target_fields_parameter = self.parameterAsFields(parameters, self.OUTPUT_FIELD, context)
        action_on_duplicate = self.parameterAsEnum(parameters, self.ACTION_ON_DUPLICATE, context)

        results = {self.OUTPUT: None,
                   self.APPENDED_COUNT: None,
                   self.UPDATED_COUNT: None,
                   self.SKIPPED_COUNT: None}

        target_value_dict = dict()
        source_field_unique_values = ''
        target_field_unique_values = ''
        source_field_type = None
        target_field_type = None

        if source_fields_parameter:
            source_field_unique_values = source_fields_parameter[0]
            source_field_type = source.fields().field(source_field_unique_values).type()

        if target_fields_parameter:
            target_field_unique_values = target_fields_parameter[0]
            target_field_type = target.fields().field(target_field_unique_values).type()

        if source_field_type != target_field_type:
            feedback.pushInfo("\nWARNING: Source and target fields to compare have different field types.")

        if source_field_unique_values and target_field_unique_values and action_on_duplicate == self.NO_ACTION:
            feedback.reportError("\nWARNING: Since you have chosen source and target fields to compare, you need to choose a valid action to apply on duplicate features before running this algorithm.")
            return results

        if action_on_duplicate != self.NO_ACTION and not (source_field_unique_values and target_field_unique_values):
            feedback.reportError("\nWARNING: Since you have chosen an action on duplicate features, you need to choose both source and target fields for comparing values before running this algorithm.")
            return results

        caps = target.dataProvider().capabilities()
        if not(caps & QgsVectorDataProvider.AddFeatures):
            feedback.reportError("\nWARNING: The target layer does not support appending features to it! Choose another target layer.")
            return results

        if action_on_duplicate == self.UPDATE_EXISTING_FEATURE and not(caps & QgsVectorDataProvider.ChangeAttributeValues and caps & QgsVectorDataProvider.ChangeGeometries):
            feedback.reportError("\nWARNING: The target layer does not support updating its features! Choose another action for duplicate features or choose another target layer.")
            return results

        editable_before = False
        if target.isEditable():
            editable_before = True
            feedback.reportError("\nWARNING: You need to close the edit session on layer '{}' before running this algorithm.".format(
                target.name()
            ))
            return results

        # Define a mapping between source and target layer
        mapping = dict()
        for target_idx in target.fields().allAttributesList():
            target_field = target.fields().field(target_idx)
            source_idx = source.fields().indexOf(target_field.name())
            if source_idx != -1:
                mapping[target_idx] = source_idx

        # Build dict of target field values so that we can search easily later {value1: [id1, id2], ...}
        if target_field_unique_values:
            for f in target.getFeatures():
                if f[target_field_unique_values] in target_value_dict:
                    target_value_dict[f[target_field_unique_values]].append(int(f.id()))
                else:
                    target_value_dict[f[target_field_unique_values]] = [int(f.id())]

        # Prepare features for the Copy and Paste
        results[self.APPENDED_COUNT] = 0
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()
        destType = target.geometryType()
        destIsMulti = QgsWkbTypes.isMultiType(target.wkbType())
        new_features = list()
        updated_features = dict()
        updated_geometries = dict()
        updated_features_count = 0
        updated_geometries_count = 0
        skipped_features_count = 0  # To properly count features that were skipped
        duplicate_features_set = set()  # To properly count features that were updated
        for current, in_feature in enumerate(features):
            if feedback.isCanceled():
                break

            target_feature_exists = False
            duplicate_target_value = None

            # If skip is the action, skip as soon as possible
            if source_field_unique_values:
                duplicate_target, duplicate_target_value = self.find_duplicate_value(
                    in_feature[source_field_unique_values],
                    source_field_type,
                    target_value_dict,
                    target_field_type)
                if duplicate_target:
                    if action_on_duplicate == self.SKIP_FEATURE:
                        request = QgsFeatureRequest(target_value_dict[duplicate_target_value])  # Get target feature ids
                        request.setFlags(QgsFeatureRequest.NoGeometry)
                        request.setSubsetOfAttributes([])  # Note: this adds a new flag
                        skipped_features_count += len([f for f in target.getFeatures(request)])
                        continue

                    target_feature_exists = True

            attrs = {target_idx: in_feature[source_idx] for target_idx, source_idx in mapping.items()}

            geom = QgsGeometry()

            if in_feature.hasGeometry() and target.isSpatial():
                # Convert geometry to match destination layer
                # Adapted from QGIS qgisapp.cpp, pasteFromClipboard()
                geom = in_feature.geometry()

                if not geom.isNull():
                    if destType != QgsWkbTypes.UnknownGeometry:
                        newGeometry = geom.convertToType(destType, destIsMulti)

                        if newGeometry.isNull():
                            continue  # Couldn't convert
                        geom = newGeometry

                    # Avoid intersection if enabled in digitize settings
                    geom.avoidIntersections(QgsProject.instance().avoidIntersectionsLayers())

            if target_feature_exists and action_on_duplicate == self.UPDATE_EXISTING_FEATURE:
                for t_f in target.getFeatures(target_value_dict[duplicate_target_value]):
                    duplicate_features_set.add(t_f.id())
                    updated_features[t_f.id()] = attrs
                    if target.isSpatial():
                        updated_geometries[t_f.id()] = geom
            else:  # Append
                new_feature = QgsVectorLayerUtils().createFeature(target, geom, attrs)
                new_features.append(new_feature)

            feedback.setProgress(int(current * total))

        # Do the Copy and Paste
        res_add_features = False
        try:
            with edit(target):
                target.beginEditCommand("Appending/Updating features...")

                if updated_features:
                    for k, v in updated_features.items():
                        if target.changeAttributeValues(k, v):
                            updated_features_count += 1
                        else:
                            feedback.reportError("\nERROR: Target feature (id={}) couldn't be updated to the following attributes: {}.".format(k, v))

                if updated_geometries:
                    for k,v in updated_geometries.items():
                        if target.changeGeometry(k, v):
                            updated_geometries_count += 1
                        else:
                            feedback.reportError("\nERROR: Target feature's geometry (id={}) couldn't be updated.".format(k))

                if new_features:
                    res_add_features = target.addFeatures(new_features)

                target.endEditCommand()
        except QgsEditError as e:
            if not editable_before:
                # Let's close the edit session to prepare for a next run
                target.rollBack()

            feedback.reportError("\nERROR: No features could be appended/updated to/in '{}', because of the following error:\n{}\n".format(
                target.name(),
                repr(e)
            ))
            return results


        if action_on_duplicate == self.SKIP_FEATURE:
            feedback.pushInfo("\nSKIPPED FEATURES: {} duplicate features were skipped while copying features to '{}'!".format(
                skipped_features_count,
                target.name()
            ))
            results[self.SKIPPED_COUNT] = skipped_features_count

        if action_on_duplicate == self.UPDATE_EXISTING_FEATURE:
            feedback.pushInfo("\nUPDATED FEATURES: {} out of {} duplicate features were updated while copying features to '{}'!".format(
                updated_features_count,
                len(duplicate_features_set),
                target.name()
            ))
            results[self.UPDATED_COUNT] = updated_features_count

        if not new_features:
            feedback.pushInfo("\nFINISHED WITHOUT APPENDED FEATURES: There were no features to append to '{}'.".format(
                target.name()
            ))
        else:
            if res_add_features:
                feedback.pushInfo("\nAPPENDED FEATURES: {} out of {} features from input layer were successfully appended to '{}'!".format(
                    len(new_features),
                    source.featureCount(),
                    target.name()
                ))
                results[self.APPENDED_COUNT] = len(new_features)
            else: # TODO do we really need this else message below?
                feedback.reportError("\nERROR: The {} features from input layer could not be appended to '{}'. Sometimes this might be due to NOT NULL constraints that are not met.".format(
                    source.featureCount(),
                    target.name()
                ))

        results[self.OUTPUT] = target
        return results
Example #42
0
    def test_value_exists(self):
        layer = createLayerWithOnePoint()
        # add some more features
        f1 = QgsFeature(2)
        f1.setAttributes(["test1", 124])
        f2 = QgsFeature(3)
        f2.setAttributes(["test2", 125])
        f3 = QgsFeature(4)
        f3.setAttributes(["test3", 126])
        f4 = QgsFeature(5)
        f4.setAttributes(["test4", 127])
        layer.dataProvider().addFeatures([f1, f2, f3, f4])

        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test4'))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'not present!'))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 123))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 124))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 127))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 99))

        # no layer
        self.assertFalse(QgsVectorLayerUtils.valueExists(None, 1, 123))
        # bad field indexes
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, -1, 'test'))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 100, 'test'))

        # with ignore list
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5]))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [999999]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [2]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [99999, 2]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 0, 'test1', [3, 4, 5, 2]))

        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5]))
        self.assertTrue(QgsVectorLayerUtils.valueExists(layer, 1, 125, [999999]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [3]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [99999, 3]))
        self.assertFalse(QgsVectorLayerUtils.valueExists(layer, 1, 125, [2, 4, 5, 3]))
Example #43
0
def execute_in_place_run(alg, parameters, context=None, feedback=None, raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :type context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :type feedback: QgsProcessingFeedback, optional
    :param raise_exceptions: useful for testing, if True exceptions are raised, normally exceptions will be forwarded to the feedback
    :type raise_exceptions: boo, default to False
    :raises QgsProcessingException: raised when there is no active layer, or it cannot be made editable
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    # Only feature based algs have sourceFlags
    try:
        if alg.sourceFlags() & QgsProcessingFeatureSource.FlagSkipGeometryValidityChecks:
            context.setInvalidGeometryCheck(QgsFeatureRequest.GeometryNoCheck)
    except AttributeError:
        pass

    active_layer = parameters['INPUT']

    # Run some checks and prepare the layer for in-place execution by:
    # - getting the active layer and checking that it is a vector
    # - making the layer editable if it was not already
    # - selecting all features if none was selected
    # - checking in-place support for the active layer/alg/parameters
    # If one of the check fails and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        if active_layer is None:
            raise QgsProcessingException(tr("There is not active layer."))

        if not isinstance(active_layer, QgsVectorLayer):
            raise QgsProcessingException(tr("Active layer is not a vector layer."))

        if not active_layer.isEditable():
            if not active_layer.startEditing():
                raise QgsProcessingException(tr("Active layer is not editable (and editing could not be turned on)."))

        if not alg.supportInPlaceEdit(active_layer):
            raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
    except QgsProcessingException as e:
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)
        return False, {}

    if not active_layer.selectedFeatureIds():
        active_layer.selectAll()

    # Make sure we are working on selected features only
    parameters['INPUT'] = QgsProcessingFeatureSourceDefinition(active_layer.id(), True)
    parameters['OUTPUT'] = 'memory:'

    req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
    req.setFlags(QgsFeatureRequest.NoGeometry)
    req.setSubsetOfAttributes([])

    # Start the execution
    # If anything goes wrong and raise_exceptions is True an exception
    # is raised, else the execution is aborted and the error reported in
    # the feedback
    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
            field_idxs = range(len(active_layer.fields()))
            iterator_req = QgsFeatureRequest(active_layer.selectedFeatureIds())
            iterator_req.setInvalidGeometryCheck(context.invalidGeometryCheck())
            feature_iterator = active_layer.getFeatures(iterator_req)
            step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context, feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer)

                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                    new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle

            # There is no way to know if some features have been skipped
            # due to invalid geometries
            if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
                selected_ids = active_layer.selectedFeatureIds()
            else:
                selected_ids = []

            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle

                new_features = []

                # Check if there are any skipped features
                if context.invalidGeometryCheck() == QgsFeatureRequest.GeometrySkipInvalid:
                    missing_ids = list(set(selected_ids) - set(result_layer.allFeatureIds()))
                    if missing_ids:
                        for f in active_layer.getFeatures(QgsFeatureRequest(missing_ids)):
                            if not f.geometry().isGeosValid():
                                new_features.append(f)

                active_layer.deleteFeatures(active_layer.selectedFeatureIds())

                for f in result_layer.getFeatures():
                    new_features.extend(QgsVectorLayerUtils.
                                        makeFeaturesCompatible([f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)), fatalError=True)

    return False, {}
Example #44
0
def execute_in_place_run(alg, active_layer, parameters, context=None, feedback=None, raise_exceptions=False):
    """Executes an algorithm modifying features in-place in the input layer.

    The input layer must be editable or an exception is raised.

    :param alg: algorithm to run
    :type alg: QgsProcessingAlgorithm
    :param active_layer: the editable layer
    :type active_layer: QgsVectoLayer
    :param parameters: parameters of the algorithm
    :type parameters: dict
    :param context: context, defaults to None
    :param context: QgsProcessingContext, optional
    :param feedback: feedback, defaults to None
    :param feedback: QgsProcessingFeedback, optional
    :raises QgsProcessingException: raised when the layer is not editable or the layer cannot be found in the current project
    :return: a tuple with true if success and results
    :rtype: tuple
    """

    if feedback is None:
        feedback = QgsProcessingFeedback()
    if context is None:
        context = dataobjects.createContext(feedback)

    if active_layer is None or not active_layer.isEditable():
        raise QgsProcessingException(tr("Layer is not editable or layer is None."))

    if not alg.supportInPlaceEdit(active_layer):
        raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))

    parameters['OUTPUT'] = 'memory:'

    try:
        new_feature_ids = []

        active_layer.beginEditCommand(alg.displayName())

        req = QgsFeatureRequest(QgsExpression(r"$id < 0"))
        req.setFlags(QgsFeatureRequest.NoGeometry)
        req.setSubsetOfAttributes([])

        # Checks whether the algorithm has a processFeature method
        if hasattr(alg, 'processFeature'):  # in-place feature editing
            # Make a clone or it will crash the second time the dialog
            # is opened and run
            alg = alg.create()
            if not alg.prepare(parameters, context, feedback):
                raise QgsProcessingException(tr("Could not prepare selected algorithm."))
            # Check again for compatibility after prepare
            if not alg.supportInPlaceEdit(active_layer):
                raise QgsProcessingException(tr("Selected algorithm and parameter configuration are not compatible with in-place modifications."))
            field_idxs = range(len(active_layer.fields()))
            feature_iterator = active_layer.getFeatures(QgsFeatureRequest(active_layer.selectedFeatureIds())) if parameters['INPUT'].selectedFeaturesOnly else active_layer.getFeatures()
            step = 100 / len(active_layer.selectedFeatureIds()) if active_layer.selectedFeatureIds() else 1
            for current, f in enumerate(feature_iterator):
                feedback.setProgress(current * step)
                if feedback.isCanceled():
                    break

                # need a deep copy, because python processFeature implementations may return
                # a shallow copy from processFeature
                input_feature = QgsFeature(f)
                new_features = alg.processFeature(input_feature, context, feedback)
                new_features = QgsVectorLayerUtils.makeFeaturesCompatible(new_features, active_layer)
                if len(new_features) == 0:
                    active_layer.deleteFeature(f.id())
                elif len(new_features) == 1:
                    new_f = new_features[0]
                    if not f.geometry().equals(new_f.geometry()):
                        active_layer.changeGeometry(f.id(), new_f.geometry())
                    if f.attributes() != new_f.attributes():
                        active_layer.changeAttributeValues(f.id(), dict(zip(field_idxs, new_f.attributes())), dict(zip(field_idxs, f.attributes())))
                    new_feature_ids.append(f.id())
                else:
                    active_layer.deleteFeature(f.id())
                    # Get the new ids
                    old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    if not active_layer.addFeatures(new_features):
                        raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                    new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                    new_feature_ids += list(new_ids - old_ids)

            results, ok = {}, True

        else:  # Traditional 'run' with delete and add features cycle
            results, ok = alg.run(parameters, context, feedback)

            if ok:
                result_layer = QgsProcessingUtils.mapLayerFromString(results['OUTPUT'], context)
                # TODO: check if features have changed before delete/add cycle
                active_layer.deleteFeatures(active_layer.selectedFeatureIds())
                new_features = []
                for f in result_layer.getFeatures():
                    new_features.extend(QgsVectorLayerUtils.
                                        makeFeaturesCompatible([f], active_layer))

                # Get the new ids
                old_ids = set([f.id() for f in active_layer.getFeatures(req)])
                if not active_layer.addFeatures(new_features):
                    raise QgsProcessingException(tr("Error adding processed features back into the layer."))
                new_ids = set([f.id() for f in active_layer.getFeatures(req)])
                new_feature_ids += list(new_ids - old_ids)

        active_layer.endEditCommand()

        if ok and new_feature_ids:
            active_layer.selectByIds(new_feature_ids)
        elif not ok:
            active_layer.rollBack()

        return ok, results

    except QgsProcessingException as e:
        active_layer.endEditCommand()
        active_layer.rollBack()
        if raise_exceptions:
            raise e
        QgsMessageLog.logMessage(str(sys.exc_info()[0]), 'Processing', Qgis.Critical)
        if feedback is not None:
            feedback.reportError(getattr(e, 'msg', str(e)))

    return False, {}
Example #45
0
    def get_boundary_points_not_covered_by_boundary_nodes(
            self, db, boundary_point_layer, boundary_layer, point_bfs_layer,
            error_layer, id_field):
        dict_uuid_boundary = {
            f[id_field]: f[db.names.T_ILI_TID_F]
            for f in boundary_layer.getFeatures()
        }
        dict_uuid_boundary_point = {
            f[id_field]: f[db.names.T_ILI_TID_F]
            for f in boundary_point_layer.getFeatures()
        }

        tmp_boundary_nodes_layer = processing.run("native:extractvertices", {
            'INPUT': boundary_layer,
            'OUTPUT': 'memory:'
        })['OUTPUT']

        # layer is created with unique vertices
        # It is necessary because 'remove duplicate vertices' processing algorithm does not filter the data as we need them
        boundary_nodes_layer = QgsVectorLayer(
            "Point?crs={}".format(boundary_layer.sourceCrs().authid()),
            'unique boundary nodes', "memory")
        data_provider = boundary_nodes_layer.dataProvider()
        data_provider.addAttributes([QgsField(id_field, QVariant.Int)])
        boundary_nodes_layer.updateFields()

        id_field_idx = tmp_boundary_nodes_layer.fields().indexFromName(
            id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])

        filter_fs = []
        fs = []
        for f in tmp_boundary_nodes_layer.getFeatures(request):
            item = [f[id_field], f.geometry().asWkt()]
            if item not in filter_fs:
                filter_fs.append(item)
                fs.append(f)
        del filter_fs
        boundary_nodes_layer.dataProvider().addFeatures(fs)

        # Spatial Join between boundary_points and boundary_nodes
        spatial_join_layer = processing.run(
            "qgis:joinattributesbylocation",
            {
                'INPUT': boundary_point_layer,
                'JOIN': boundary_nodes_layer,
                'PREDICATE': [0],  # Intersects
                'JOIN_FIELDS': [db.names.T_ID_F],
                'METHOD': 0,
                'DISCARD_NONMATCHING': False,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        # create dict with layer data
        id_field_idx = boundary_point_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_boundary_point = {
            feature[id_field]: feature
            for feature in boundary_point_layer.getFeatures(request)
        }

        exp_point_bfs = '"{}" is not null and "{}" is not null'.format(
            db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F,
            db.names.POINT_BFS_T_LC_BOUNDARY_F)
        list_point_bfs = [{
            'boundary_point_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F],
            'boundary_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_F]
        } for feature in point_bfs_layer.getFeatures(exp_point_bfs)]

        spatial_join_boundary_point_boundary_node = [{
            'boundary_point_id':
            feature[id_field],
            'boundary_id':
            feature[id_field + '_2']
        } for feature in spatial_join_layer.getFeatures()]

        boundary_point_without_boundary_node = list()
        no_register_point_bfs = list()
        duplicate_in_point_bfs = list()

        # point_bfs topology check
        for item_sj in spatial_join_boundary_point_boundary_node:
            boundary_point_id = item_sj['boundary_point_id']
            boundary_id = item_sj['boundary_id']

            if boundary_id != NULL:
                if item_sj not in list_point_bfs:
                    no_register_point_bfs.append(
                        (boundary_point_id,
                         boundary_id))  # no registered in point bfs
                elif list_point_bfs.count(item_sj) > 1:
                    duplicate_in_point_bfs.append(
                        (boundary_point_id,
                         boundary_id))  # duplicate in point bfs
            else:
                boundary_point_without_boundary_node.append(
                    boundary_point_id)  # boundary point without boundary node

        features = list()

        # boundary point without boundary node
        if boundary_point_without_boundary_node is not None:
            for item in boundary_point_without_boundary_node:
                boundary_point_id = item  # boundary_point_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        None,
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100301),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100301
                    })
                features.append(new_feature)

        # No registered in point_bfs
        if no_register_point_bfs is not None:
            for error_no_register in set(no_register_point_bfs):
                boundary_point_id = error_no_register[0]  # boundary_point_id
                boundary_id = error_no_register[1]  # boundary_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        dict_uuid_boundary.get(boundary_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100302),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100302
                    })
                features.append(new_feature)

        # Duplicate in point_bfs
        if duplicate_in_point_bfs is not None:
            for error_duplicate in set(duplicate_in_point_bfs):
                boundary_point_id = error_duplicate[0]  # boundary_point_id
                boundary_id = error_duplicate[1]  # boundary_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        dict_uuid_boundary.get(boundary_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100303),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100303
                    })
                features.append(new_feature)

        return features
Example #46
0
    def get_boundary_nodes_features_not_covered_by_boundary_points(
            self, db, boundary_point_layer, boundary_layer, point_bfs_layer,
            error_layer, id_field):
        dict_uuid_boundary_point = get_uuid_dict(boundary_point_layer,
                                                 db.names, db.names.T_ID_F)
        dict_uuid_boundary = get_uuid_dict(boundary_layer, db.names,
                                           db.names.T_ID_F)
        tmp_boundary_nodes_layer = processing.run("native:extractvertices", {
            'INPUT': boundary_layer,
            'OUTPUT': 'memory:'
        })['OUTPUT']

        # layer is created with unique vertices, it is necessary because 'remove duplicate vertices' processing algorithm does not filter the data as we need them
        boundary_nodes_unique_layer = QgsVectorLayer(
            "Point?crs={}".format(boundary_layer.sourceCrs().authid()),
            'unique boundary nodes', "memory")
        data_provider = boundary_nodes_unique_layer.dataProvider()
        data_provider.addAttributes([QgsField(id_field, QVariant.Int)])
        boundary_nodes_unique_layer.updateFields()

        id_field_idx = tmp_boundary_nodes_layer.fields().indexFromName(
            id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])

        filter_fs = list()
        fs = list()
        for f in tmp_boundary_nodes_layer.getFeatures(request):
            item = [f[id_field], f.geometry().asWkt()]
            if item not in filter_fs:
                filter_fs.append(item)
                fs.append(f)
        boundary_nodes_unique_layer.dataProvider().addFeatures(fs)

        # Create an autoincremental field to have an identifying field
        boundary_nodes_layer = processing.run(
            "native:addautoincrementalfield", {
                'INPUT': boundary_nodes_unique_layer,
                'FIELD_NAME': 'AUTO',
                'START': 0,
                'GROUP_FIELDS': [],
                'SORT_EXPRESSION': '',
                'SORT_ASCENDING': True,
                'SORT_NULLS_FIRST': False,
                'OUTPUT': 'memory:'
            })['OUTPUT']
        GeometryUtils.create_spatial_index(boundary_nodes_layer)

        # Spatial Join between boundary_nodes and boundary_points
        spatial_join_layer = processing.run(
            "qgis:joinattributesbylocation",
            {
                'INPUT': boundary_nodes_layer,
                'JOIN': boundary_point_layer,
                'PREDICATE': [0],  # Intersects
                'JOIN_FIELDS': [db.names.T_ID_F],
                'METHOD': 0,
                'DISCARD_NONMATCHING': False,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        # create dict with layer data
        id_field_idx = boundary_nodes_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_boundary_nodes = {
            feature['AUTO']: feature
            for feature in boundary_nodes_layer.getFeatures(request)
        }

        exp_point_bfs = '"{}" is not null and "{}" is not null'.format(
            db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F,
            db.names.POINT_BFS_T_LC_BOUNDARY_F)
        list_point_bfs = [{
            'boundary_point_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F],
            'boundary_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_F]
        } for feature in point_bfs_layer.getFeatures(exp_point_bfs)]

        list_spatial_join_boundary_node_boundary_point = [{
            'boundary_point_id':
            feature[id_field + '_2'],
            'boundary_node_id':
            feature['AUTO']
        } for feature in spatial_join_layer.getFeatures()]

        boundary_node_without_boundary_point = list()
        no_register_point_bfs = list()
        duplicate_in_point_bfs = list()

        # point_bfs topology check
        for item_sj in list_spatial_join_boundary_node_boundary_point:
            boundary_node_id = item_sj['boundary_node_id']
            boundary_point_id = item_sj['boundary_point_id']

            if boundary_point_id != NULL:

                boundary_id = dict_boundary_nodes[boundary_node_id][
                    id_field]  # get boundary id
                item_sj_check = {
                    'boundary_point_id': boundary_point_id,
                    'boundary_id': boundary_id
                }  # dict to check

                if item_sj_check not in list_point_bfs:
                    no_register_point_bfs.append(
                        (boundary_point_id,
                         boundary_node_id))  # no registered in point bfs
                elif list_point_bfs.count(item_sj_check) > 1:
                    duplicate_in_point_bfs.append(
                        (boundary_point_id,
                         boundary_node_id))  # duplicate in point bfs
            else:
                boundary_node_without_boundary_point.append(
                    boundary_node_id)  # boundary node without boundary point

        features = list()

        # boundary node without boundary point
        if boundary_node_without_boundary_point is not None:
            for item in boundary_node_without_boundary_point:
                boundary_node_id = item
                boundary_node_geom = dict_boundary_nodes[
                    boundary_node_id].geometry()
                boundary_id = dict_boundary_nodes[boundary_node_id][
                    id_field]  # get boundary id
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_node_geom, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        None,
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200401),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200401
                    })
                features.append(new_feature)

        # Duplicate in point_bfs
        if duplicate_in_point_bfs is not None:
            for error_duplicate in set(duplicate_in_point_bfs):
                boundary_point_id = error_duplicate[0]
                boundary_node_id = error_duplicate[1]
                boundary_node_geom = dict_boundary_nodes[
                    boundary_node_id].geometry()
                boundary_id = dict_boundary_nodes[boundary_node_id][
                    id_field]  # get boundary id
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_node_geom, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200403),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200403
                    })
                features.append(new_feature)

        # No registered in point_bfs
        if no_register_point_bfs is not None:
            for error_no_register in set(no_register_point_bfs):
                boundary_point_id = error_no_register[0]
                boundary_node_id = error_no_register[1]
                boundary_node_geom = dict_boundary_nodes[
                    boundary_node_id].geometry()
                boundary_id = dict_boundary_nodes[boundary_node_id][
                    id_field]  # get boundary id
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_node_geom, {
                        0:
                        dict_uuid_boundary.get(boundary_id),
                        1:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E200402),
                        3:
                        QUALITY_RULE_ERROR_CODE_E200402
                    })
                features.append(new_feature)

        return features
Example #47
0
    def __check_overlapping_points(self, db, rule, layer_dict, error_code):
        """
        Shows which points are overlapping
        :param db: db connection instance
        :param layer_dict: Dict with layer name and layer object
        :return: msg, Qgis.MessageLevel
        """
        features = []
        layer_name = list(layer_dict[QUALITY_RULE_LAYERS].keys()
                          )[0] if layer_dict[QUALITY_RULE_LAYERS] else None
        point_layer = list(layer_dict[QUALITY_RULE_LAYERS].values()
                           )[0] if layer_dict[QUALITY_RULE_LAYERS] else None
        if not point_layer:
            return QCoreApplication.translate(
                "PointQualityRules",
                "'{}' layer not found!").format(layer_name), Qgis.Critical

        if point_layer.featureCount() == 0:
            return (QCoreApplication.translate(
                "PointQualityRules",
                "There are no points in layer '{}' to check for overlaps!").
                    format(layer_name), Qgis.Warning)

        else:
            error_layer = QgsVectorLayer(
                "Point?crs={}".format(point_layer.sourceCrs().authid()),
                rule.error_table_name, "memory")
            data_provider = error_layer.dataProvider()
            data_provider.addAttributes(rule.error_table_fields)
            error_layer.updateFields()

            overlapping = self.geometry.get_overlapping_points(point_layer)
            flat_overlapping = [id for items in overlapping
                                for id in items]  # Build a flat list of ids

            dict_uuids = {
                f.id(): f[db.names.T_ILI_TID_F]
                for f in point_layer.getFeatures(flat_overlapping)
            }

            for items in overlapping:
                # We need a feature geometry, pick the first id to get it
                feature = point_layer.getFeature(items[0])
                point = feature.geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, point, {
                        0: ", ".join([str(dict_uuids.get(i)) for i in items]),
                        1: len(items),
                        2: self.quality_rules_manager.get_error_message(
                            error_code),
                        3: error_code
                    })
                features.append(new_feature)

            error_layer.dataProvider().addFeatures(features)

            if error_layer.featureCount() > 0:
                added_layer = self.app.gui.add_error_layer(db, error_layer)
                return (QCoreApplication.translate(
                    "PointQualityRules",
                    "A memory layer with {} overlapping points in '{}' has been added to the map!"
                ).format(added_layer.featureCount(),
                         layer_name), Qgis.Critical)
            else:
                return (QCoreApplication.translate(
                    "PointQualityRules",
                    "There are no overlapping points in layer '{}'!").format(
                        layer_name), Qgis.Success)
Example #48
0
    def finish_group_party_saving(self, members, layer_id, features):
        try:
            self._layers[LA_GROUP_PARTY_TABLE][
                LAYER].committedFeaturesAdded.disconnect()
        except TypeError as e:
            pass

        message = QCoreApplication.translate(
            self.WIZARD_NAME,
            "'{}' tool has been closed because an error occurred while trying to save the data."
        ).format(self.WIZARD_TOOL_NAME)
        if len(features) != 1:
            message = QCoreApplication.translate(
                self.WIZARD_NAME,
                "'{}' tool has been closed. We should have got only one group party... We cannot do anything with {} group parties"
            ).format(self.WIZARD_TOOL_NAME, len(features))
            self.log.logMessage(
                "We should have got only one group party... We cannot do anything with {} group parties"
                .format(len(features)), PLUGIN_NAME, Qgis.Warning)
        else:
            fid = features[0].id()
            if not self._layers[LA_GROUP_PARTY_TABLE][LAYER].getFeature(
                    fid).isValid():
                self.log.logMessage(
                    "Feature not found in table Group Party...", PLUGIN_NAME,
                    Qgis.Warning)
            else:
                group_party_id = self._layers[LA_GROUP_PARTY_TABLE][
                    LAYER].getFeature(fid)[ID_FIELD]

                # Now save members
                party_ids = list()
                for party_id, fraction in members.items():
                    # Create connections to react when a group party is stored to the DB
                    self._layers[MEMBERS_TABLE][
                        LAYER].committedFeaturesAdded.connect(
                            partial(self.finish_member_saving, fraction))

                    new_feature = QgsVectorLayerUtils().createFeature(
                        self._layers[MEMBERS_TABLE][LAYER])
                    new_feature.setAttribute(MEMBERS_GROUP_PARTY_FIELD,
                                             group_party_id)
                    new_feature.setAttribute(MEMBERS_PARTY_FIELD, party_id)
                    self.log.logMessage(
                        "Saving group party's member ({}: {}).".format(
                            group_party_id, party_id), PLUGIN_NAME, Qgis.Info)
                    with edit(self._layers[MEMBERS_TABLE][LAYER]):
                        self._layers[MEMBERS_TABLE][LAYER].addFeature(
                            new_feature)
                        party_ids.append(party_id)

                if len(party_ids):
                    message = QCoreApplication.translate(
                        self.WIZARD_NAME,
                        "The new group party (t_id={}) was successfully created and associated with its corresponding party(ies) (t_id={})!"
                    ).format(group_party_id,
                             ", ".join([str(b) for b in party_ids]))
                else:
                    message = QCoreApplication.translate(
                        self.WIZARD_NAME,
                        "The new group party (t_id={}) was successfully created but this one wasn't associated with a party(ies)"
                    ).format(group_party_id)
        self.close_wizard(message)
Example #49
0
    def test_validate_attribute(self):
        """ test validating attributes against constraints """
        layer = createLayerWithOnePoint()

        # field expression check
        layer.setConstraintExpression(1, 'fldint>5')

        f = QgsFeature(2)
        f.setAttributes(["test123", 6])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        f.setAttributes(["test123", 2])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)
        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # bad field expression check
        layer.setConstraintExpression(1, 'fldint>')
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        layer.setConstraintExpression(1, None)

        # not null constraint
        f.setAttributes(["test123", NULL])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # unique constraint
        f.setAttributes(["test123", 123])
        layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)
        print(errors)

        # checking only for provider constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1, origin=QgsFieldConstraints.ConstraintOriginProvider)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # checking only for soft constraints
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique, QgsFieldConstraints.ConstraintStrengthHard)
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1, strength=QgsFieldConstraints.ConstraintStrengthSoft)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)
        # checking for hard constraints
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1,
                                                            strength=QgsFieldConstraints.ConstraintStrengthHard)
        self.assertFalse(res)
        self.assertEqual(len(errors), 1)

        # check - same id should be ignored when testing for uniqueness
        f1 = QgsFeature(1)
        f1.setAttributes(["test123", 123])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f1, 1)
        self.assertTrue(res)
        self.assertEqual(len(errors), 0)

        # test double constraint failure
        layer.setConstraintExpression(1, 'fldint>5')
        layer.removeFieldConstraint(1, QgsFieldConstraints.ConstraintUnique)
        layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintNotNull)
        f.setAttributes(["test123", NULL])
        res, errors = QgsVectorLayerUtils.validateAttribute(layer, f, 1)
        self.assertFalse(res)
        self.assertEqual(len(errors), 2)
        print(errors)
    def build_boundary(self, db):
        QgsProject.instance().setAutoTransaction(False)
        layer = self.qgis_utils.get_layer_from_layer_tree(db, BOUNDARY_TABLE)
        use_selection = True

        if layer is None:
            self.qgis_utils.message_with_button_load_layer_emitted.emit(
                QCoreApplication.translate(
                    "ToolBar", "First load the layer {} into QGIS!").format(
                        BOUNDARY_TABLE),
                QCoreApplication.translate(
                    "ToolBar", "Load layer {} now").format(BOUNDARY_TABLE),
                [BOUNDARY_TABLE, None], Qgis.Warning)
            return
        else:
            if layer.selectedFeatureCount() == 0:
                reply = QMessageBox.question(
                    None, QCoreApplication.translate("ToolBar", "Continue?"),
                    QCoreApplication.translate(
                        "ToolBar",
                        "There are no selected boundaries, do you like to use all the {} boundaries in the data base?"
                    ).format(layer.featureCount()), QMessageBox.Yes,
                    QMessageBox.No)
                if reply == QMessageBox.Yes:
                    use_selection = False
                else:
                    self.qgis_utils.message_emitted.emit(
                        QCoreApplication.translate(
                            "ToolBar", "First select at least one boundary!"),
                        Qgis.Warning)
                    return

        if use_selection:
            new_boundary_geoms, boundaries_to_del_ids = self.geometry.fix_selected_boundaries(
                layer)
            num_boundaries = layer.selectedFeatureCount()
        else:
            new_boundary_geoms, boundaries_to_del_ids = self.geometry.fix_boundaries(
                layer)
            num_boundaries = layer.featureCount()

        if len(new_boundary_geoms) > 0:
            layer.startEditing(
            )  # Safe, even if layer is already on editing state

            # the boundaries that are to be replaced are removed
            layer.deleteFeatures(boundaries_to_del_ids)

            # Create features based on segment geometries
            new_fix_boundary_features = list()
            for boundary_geom in new_boundary_geoms:
                feature = QgsVectorLayerUtils().createFeature(
                    layer, boundary_geom)
                new_fix_boundary_features.append(feature)

            layer.addFeatures(new_fix_boundary_features)
            self.qgis_utils.message_emitted.emit(
                QCoreApplication.translate(
                    "ToolBar",
                    "{} feature(s) was(were) analyzed generating {} boundary(ies)!"
                ).format(num_boundaries, len(new_fix_boundary_features)),
                Qgis.Info)
            self.iface.mapCanvas().refresh()
        else:
            self.qgis_utils.message_emitted.emit(
                QCoreApplication.translate(
                    "ToolBar", "There are no boundaries to build."), Qgis.Info)