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())
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))
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])
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])
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])
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])
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)
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
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
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}
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))
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()
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) ))
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
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)
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
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')
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]))
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])
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])
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])
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
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
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 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)
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 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')
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)
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
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]))
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 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, {}
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
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
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)
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)
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)