예제 #1
0
    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 do not take precedence over passed attribute values
        f = QgsVectorLayerUtils.createFeature(vl,
                                              attributes={
                                                  1: 'qgis is great',
                                                  0: 3
                                              })
        self.assertEqual(f.attributes(), [3, "qgis is great", 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 great", 5, 12, None])
예제 #2
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

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

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

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

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

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # 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])
예제 #3
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

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

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

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

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

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

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

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

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

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

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

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

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

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

        # test take vector layer default value expression overrides postgres provider default clause
        vl.setDefaultValueExpression(3, "'mappy'")
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
        self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'mappy', None, None])
예제 #6
0
    def testVectorLayerUtilsCreateFeatureWithProviderDefault(self):
        vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres")
        default_clause = 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)'
        self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause)

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

        # test take vector layer default value expression overrides postgres provider default clause
        vl.setDefaultValueDefinition(3, QgsDefaultValue("'mappy'"))
        f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'})
        self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'mappy', None, None])
    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 createFeature(self, geom):
        provider = self.__layer.dataProvider()

        if not self.__isEditMode:
            feature = QgsVectorLayerUtils.createFeature(self.__layer)
        else:
            feature = self.__layer.getFeature(self.feature_id)

        if not (geom.validateGeometry()):
            feature.setGeometry(geom)
        else:
            reply = QMessageBox.question(self.iface.mainWindow(),
                                         self.translate_str("Feature not valid"),
                                         self.translate_str("The new geometry of the feature isn't "
                                                            "valid. Do you want to use it anyway?"),
                                         QMessageBox.Yes, QMessageBox.No)
            if reply == QMessageBox.Yes:
                feature.setGeometry(geom)
            else:
                return False

        if not self.__isEditMode:
            self.__layer.beginEditCommand("Feature added")
            self.__layer.addFeature(feature)
            if self.iface.openFeatureForm(self.__layer, feature):
                self.__layer.endEditCommand()
            else:
                self.__layer.destroyEditCommand()
        else:
            self.__layer.beginEditCommand("Feature updated")
            self.__layer.updateFeature(feature)
            self.__layer.endEditCommand()

        self.canvas.refresh()
    def addFeature(self, dest_layer):
        geometry_canvas = self.geom_poly

        for l in self.getVisibleLayers():
            geometry_canvas = self.getDifferenceGeometry(geometry_canvas, l)

        # dump geom if it s multipart
        geometry_parts = [p for p in geometry_canvas.parts()]
        for part in geometry_parts:
            part_wkt = part.asWkt()
            part_geom = QgsGeometry.fromWkt(part_wkt)

            if part_geom.isGeosValid() == False:
                part_geom = part_geom.makeValid()

            if part_geom.isGeosValid() == True and part_geom.area() > 0.1:
                self.addTopologicalPointsForActiveLayers(part_geom)

                feature = QgsVectorLayerUtils.createFeature(dest_layer)
                feature.setGeometry(part_geom)

                height_col_idx = dest_layer.fields().indexFromName('SZEROKOSC')

                if height_col_idx != -1:
                    feature.setAttribute(height_col_idx, self.dist)

                if not dest_layer.isEditable():
                    dest_layer.startEditing()

                dest_layer.addFeature(feature)

                self.iface.openFeatureForm(dest_layer, feature, False)
예제 #10
0
    def linkFeatureManyToManyPolymorphic(self):

        nmRelations = dict()
        for relation in self._polymorphicRelation.generateRelations():
            nmRelations[relation.referencedLayer().name()] = relation

        layerName, ok = QInputDialog.getItem(self,
                                             self.tr("Please selct a layer"),
                                             self.tr("Layer:"),
                                             nmRelations.keys())

        if not ok:
            return

        nmRelation = nmRelations[layerName]
        selectionDlg = QgsFeatureSelectionDlg(nmRelation.referencedLayer(),
                                              self.editorContext(), self)
        selectionDlg.setWindowTitle(
            self.tr("Please select the features to link. Layer: {0}").format(
                layerName))
        if not selectionDlg.exec():
            return

        # Fields of the linking table
        fields = self.relation().referencingLayer().fields()

        linkAttributes = dict()
        linkAttributes[fields.indexFromName(
            self._polymorphicRelation.referencedLayerField(
            ))] = self._polymorphicRelation.layerRepresentation(
                nmRelation.referencedLayer())
        for key in self.relation().fieldPairs():
            linkAttributes[fields.indexOf(key)] = self.feature().attribute(
                self.relation().fieldPairs()[key])

        # Expression context for the linking table
        context = self.relation().referencingLayer().createExpressionContext()

        featureIterator = nmRelation.referencedLayer().getFeatures(
            QgsFeatureRequest().setFilterFids(
                selectionDlg.selectedFeatures()).setSubsetOfAttributes(
                    nmRelation.referencedFields()))
        relatedFeature = QgsFeature()
        newFeatures = []
        while featureIterator.nextFeature(relatedFeature):
            for key in nmRelation.fieldPairs():
                linkAttributes[fields.indexOf(key)] = relatedFeature.attribute(
                    nmRelation.fieldPairs()[key])

            linkFeature = QgsVectorLayerUtils.createFeature(
                self.relation().referencingLayer(), QgsGeometry(),
                linkAttributes, context)
            newFeatures.append(linkFeature)

        self.relation().referencingLayer().addFeatures(newFeatures)
        ids = []
        for feature in newFeatures:
            ids.append(feature.id())
        self.relation().referencingLayer().selectByIds(ids)
예제 #11
0
 def addFeature(self, layer):
     feature = QgsVectorLayerUtils.createFeature(layer)
     feature.setGeometry(self.target_geom)
     layer.addFeature(feature)
     self.addTopologicalPoints(self.target_geom)
     self.canvas.refresh()
     self.reset()
     self.iface.openFeatureForm(layer, feature, False)
예제 #12
0
    def testCreateFeature(self):
        """ 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 = 'service=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()
예제 #13
0
    def identifyAndFixInvalidGeometries(self, inputLyr, fixInput=False, onlySelected=False, feedback=None):
        iterator, featCount = self.getFeatureList(inputLyr, onlySelected=onlySelected)
        stepSize = 100/featCount if featCount else 0
        flagDict = dict()
        parameterDict = self.getDestinationParameters(inputLyr)
        geometryType = inputLyr.geometryType()
        newFeatSet = set()
        if fixInput:
            inputLyr.startEditing()
            inputLyr.beginEditCommand('Fixing geometries')
        for current, feat in enumerate(iterator):
            if feedback is not None and feedback.isCanceled():
                break
            geom = feat.geometry()
            id = feat.id()
            attrMap = { idx : feat[field.name()] for idx, field in enumerate(feat.fields()) if idx not in inputLyr.primaryKeyAttributes()}
            for i, validate_type in enumerate(['GEOS', 'QGIS']):
                if feedback is not None and feedback.isCanceled():
                    break
                for error in geom.validateGeometry(i):
                    if feedback is not None and feedback.isCanceled():
                        break

                    if error.hasWhere():
                        errorPointXY = error.where()
                        flagGeom = QgsGeometry.fromPointXY(errorPointXY)
                        if geom.type() == QgsWkbTypes.LineGeometry and self.isClosedAndFlagIsAtStartOrEnd(geom, flagGeom):
                            continue
                        if errorPointXY not in flagDict:
                            flagDict[errorPointXY] = {
                                'geom' : flagGeom,
                                'reason' : ''
                            }
                        flagDict[errorPointXY]['reason'] += '{type} invalid reason: {text}\n'.format(
                            type=validate_type,
                            text=error.what()
                        )
            if fixInput:
                geom.removeDuplicateNodes(useZValues=parameterDict['hasZValues'])
                fixedGeom = geom.makeValid()
                for idx, newGeom in enumerate(self.geometryHandler.handleGeometryCollection(fixedGeom, geometryType, parameterDict=parameterDict)):
                    if idx == 0:
                        inputLyr.changeGeometry(id, newGeom)
                    else:
                        newFeat = QgsVectorLayerUtils.createFeature(inputLyr, newGeom, attrMap)
                        newFeatSet.add(newFeat)
            if feedback is not None:
                feedback.setProgress(stepSize*current)
        if fixInput:
            inputLyr.addFeatures(newFeatSet)
            inputLyr.endEditCommand()

        return flagDict
예제 #14
0
    def calc_charger(self):
        self.layerstor.chargerloclyr = QgsVectorLayer(
            'Point?crs=%s' % self.crsstor.dest_crs.authid(),
            'proposed Charger locations', 'memory')
        tempprovider = self.layerstor.chargerloclyr.dataProvider()
        self.layerstor.chargerloclyr.startEditing()
        tempprovider.addAttributes(
            [QgsField("rank", QVariant.Double, "double", 10, 3)])
        self.layerstor.chargerloclyr.commitChanges()
        self.layerstor.chargerloclyr.startEditing()
        desired_n = int(self.dlg.n_charStations_le.text())
        min_distance = int(self.dlg.min_distance_le.text())
        excluded_area_list = [
        ]  # wenn ein Punkt gefunden wird, wird ein buffer mit mindest abstand drumgelegt, der kreis wird hier rein gelegt und alle punkte die in dem drin liegen , werden anschließend ignoriert

        class p_feature():
            def __init__(self, feature):
                self.feature = feature

            def __lt__(self, other):
                return (self.feature["score"] < other.feature["score"])

        feature_list = []
        for f in self.layerstor.temprasterpunktlyr.getFeatures():
            feature_list.append(p_feature(f))
        feature_list.sort(reverse=True)
        defined_positions = 0
        for f in feature_list:
            if defined_positions == desired_n:
                break
            print([
                buffer.contains(f.feature.geometry().asPoint())
                for buffer in excluded_area_list
            ])
            if sum([
                    buffer.contains(f.feature.geometry().asPoint())
                    for buffer in excluded_area_list
            ]) == 0:
                defined_positions += 1
                excluded_area_list.append(f.feature.geometry().buffer(
                    min_distance, -1))
                nwFeature = QgsVectorLayerUtils.createFeature(
                    self.layerstor.chargerloclyr)
                nwFeature.setGeometry(f.feature.geometry())
                nwFeature["rank"] = defined_positions
                self.layerstor.chargerloclyr.addFeature(nwFeature)
        self.layerstor.chargerloclyr.commitChanges()
        self.layerstor.chargerloclyr.updateExtents()
        QgsProject.instance().addMapLayer(self.layerstor.chargerloclyr)
예제 #15
0
 def handleFeatures(self, selectedField, layer):
     layer.startEditing()
     for item in self.auxList:
         if 'featId' in item:
             feat = item['feat']
             idx = feat.fieldNameIndex(selectedField)
             feat.setAttribute(idx, item['value'])
             layer.updateFeature(feat)
         else:
             self.geometryHandler.reprojectFeature(item['geom'],
                                                   layer.crs())
             feature = QgsVectorLayerUtils.createFeature(
                 layer, item['geom'])
             self.addFeature(feature, layer, selectedField, item['value'])
     self.auxList = []
     self.canvas.refresh()
예제 #16
0
 def handleConvertedFeature(self, feat, lyr, parameterDict=None, coordinateTransformer=None):
     parameterDict = {} if parameterDict is None else parameterDict
     geomList = self.geometryHandler.handleGeometry(
         feat.geometry(), parameterDict, coordinateTransformer)
     newFeatSet = set()
     for geom in geomList:
         newFeat = QgsVectorLayerUtils.createFeature(lyr, geom)
         for idx, field in enumerate(lyr.fields()):
             fieldName = field.name()
             try:
                 if idx not in lyr.primaryKeyAttributes():
                     newFeat[fieldName] = feat[fieldName]
             except KeyError:
                 pass
         newFeatSet.add(newFeat)
     return newFeatSet
예제 #17
0
    def addCurrentCoordinatesToDigitizeSession(self):
        editTool = self.__compatibleMapTool()
        if (not editTool):
            return False

        result = False

        point = self.inputCoordinatesInCanvasCrs()
        if (editTool.mode() == QgsMapToolCapture.CaptureMode.CapturePoint):
            #coordinatorLog("Point Capture!")
            layer = self.iface.activeLayer()

            # if canvas CRS is not layer CRS we need to transform first:
            transform = QgsCoordinateTransform(
                self.canvas.mapSettings().destinationCrs(), layer.sourceCrs(),
                self._project)
            point = transform.transform(
                point, QgsCoordinateTransform.ForwardTransform)

            geometry = QgsGeometry.fromPointXY(point)
            feature = QgsVectorLayerUtils.createFeature(
                layer, geometry, {}, layer.createExpressionContext())
            if ((len(layer.fields()) < 1)
                    or self.iface.openFeatureForm(layer, feature)):
                result = layer.addFeature(feature)
                if (result):
                    #coordinatorLog("Point successfully written to layer")
                    pass
                layer.triggerRepaint()

        elif ((editTool.mode() == QgsMapToolCapture.CaptureMode.CapturePolygon)
              or
              (editTool.mode() == QgsMapToolCapture.CaptureMode.CaptureLine)):
            #coordinatorLog("Rubberband Capture!")
            result = (editTool.addVertex(point) == 0)
        else:
            return False

        if (result):
            self.dockwidget.showInfoMessage(CT.tr("coordinate added"), 1500)
        else:
            self.setWarningMessage(CT.tr("adding coordinate failed"))

        return result
예제 #18
0
 def handleConvertedFeature(self,
                            feat,
                            lyr,
                            parameterDict=None,
                            coordinateTransformer=None):
     parameterDict = {} if parameterDict is None else parameterDict
     geomList = self.geometryHandler.handleGeometry(feat.geometry(),
                                                    parameterDict,
                                                    coordinateTransformer)
     newFeatSet = set()
     for geom in geomList:
         attrMap = {
             idx: feat[field.name()]
             for idx, field in enumerate(feat.fields())
             if idx not in lyr.primaryKeyAttributes()
         }
         newFeat = QgsVectorLayerUtils.createFeature(lyr, geom, attrMap)
         newFeatSet.add(newFeat)
     return newFeatSet
예제 #19
0
 def addPoint(self):
     try:
         if self.gps_active:
             if self.actionGPSlog.isChecked(): self.gpsLog.log('addPoint')
             gtoGpsInfo = self.lastGpsInfo
             if gtoGpsInfo.isValid:
                 if self.pointLayer is not None:
                     layer = self.pointLayer
                 else:
                     layer = self.iface.activeLayer()
                 geoType = layer.geometryType()
                 if geoType != QgsWkbTypes.GeometryType.PointGeometry:
                     self.info.msg("Kein Punktlayer ausgewählt!")
                     return
                 # create feature
                 feat = QgsVectorLayerUtils.createFeature(layer)
                 tr = QgsCoordinateTransform(self.prj_crs, layer.crs(),
                                             self.prj)
                 tr_point = tr.transform(gtoGpsInfo.mapPointXY)
                 geo = QgsGeometry.fromPointXY(tr_point)
                 feat.setGeometry(geo)
                 # set attributes
                 gps_addpoint_attributes = self.settings.get(
                     "gps_addpoint_attributes", {})
                 for k, v in gps_addpoint_attributes.items():
                     feat[k] = layer.fields().field(k).convertCompatible(
                         self.dic_gpsinfo[v])
                 # add to layer
                 if self.pointLayer is not None:
                     # add to provider
                     (res,
                      outFeats) = layer.dataProvider().addFeatures([feat])
                     layer.select(outFeats[0].id())
                 else:
                     if not layer.isEditable(): layer.startEditing()
                     layer.beginEditCommand('Add stream geometry')
                     layer.addFeatures([feat])
                     layer.endEditCommand()
                     self.helper.refreshLayer(layer)
     except Exception as e:
         self.info.err(e)
예제 #20
0
    def saveGeometry(self, force=False):
        try:
            if self.streamLayer is not None:
                if not force:
                    res = self.info.gtoQuestion(
                        "GPS Stream-Geometry in Layer \n{0}\nspeichern?".
                        format(self.streamLayer.name()),
                        title='GPS Streaming',
                        btns=QMessageBox.Yes | QMessageBox.No
                        | QMessageBox.Cancel,
                        parent=self.iface.mainWindow())
                    if res == QMessageBox.Cancel: return res
                    if res == QMessageBox.No:
                        self.actionGPSsave.setEnabled(False)
                        self.streamLayer = None
                        self.rb.reset()
                        return
                # create feature
                feat = QgsVectorLayerUtils.createFeature(self.streamLayer)
                geo = self.rb.asGeometry()
                feat.setGeometry(geo)
                # add to layer
                if not self.streamLayer.isEditable():
                    self.streamLayer.startEditing()
                self.streamLayer.beginEditCommand('Add stream geometry')
                self.streamLayer.addFeatures([feat])
                self.streamLayer.endEditCommand()
                # add to provider
                # (res, outFeats) = self.streamLayer.dataProvider().addFeatures([feat])
                # self.streamLayer.select(outFeats[0].id())

                # reset streaming
                self.actionGPSsave.setEnabled(False)
                self.streamLayer = None
                self.rb.reset()
        except Exception as e:
            self.streamLayer.destroyEditCommand()
            self.info.err(e)
 def __init__(self, id, gtotool, config, debug):
     super(run, self).__init__()
     try:
         # tool data
         targetlayer = config['targetlayer']
         tools = config.get("tools", [])
         # init
         self.iface = gtotool.iface
         self.canvas = self.iface.mapCanvas()
         self.layPoly = self.iface.activeLayer()
         if self.layPoly.geometryType(
         ) == QgsWkbTypes.GeometryType.PolygonGeometry:
             layLine = QgsProject.instance().mapLayersByName(targetlayer)[0]
             if not layLine.isEditable(): layLine.startEditing()
             layLine.beginEditCommand("New feature")
             for f in self.layPoly.selectedFeatures():
                 geo = f.geometry().convertToType(QgsWkbTypes.LineGeometry)
                 # transform
                 sourceCrs = self.layPoly.crs()
                 destCrs = layLine.crs()
                 tr = QgsCoordinateTransform(sourceCrs, destCrs,
                                             QgsProject.instance())
                 geo.transform(tr)
                 # create feature
                 feature = QgsVectorLayerUtils.createFeature(layLine)
                 feature.setGeometry(geo)
                 layLine.addFeatures([feature])
                 layLine.updateFeature(feature)
                 # layLine.dataProvider().addFeatures([feature])#no roll back because no edit buffer/beginedit!
             layLine.endEditCommand()
             gtotool.gtomain.runcmd(tools)
     except Exception as e:
         gtotool.info.err(e)
         try:
             layLine.destroyEditCommand()
         except:
             pass
예제 #22
0
    def add_point(self):
        try:
            self.check_coords()
            layer = self.iface.activeLayer()
            if layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry:
                self.prj.layerTreeRoot().findLayer(layer.id()).setItemVisibilityCheckedParentRecursive(True)
                if self.x != 0 and self.y != 0:
                    feat = QgsVectorLayerUtils.createFeature(layer)
                    tr = QgsCoordinateTransform(self.prj.crs(), self.crs_layer, self.prj)
                    trPoint = tr.transform(QgsPointXY(self.x, self.y))
                    feat.setGeometry(QgsGeometry.fromPointXY(trPoint))
                    # direct save
                    # (res, features) = layer.dataProvider().addFeatures([feat])
                    # if self.debug: self.info.log("new point:", res, features[0])
                    # set attributes

                    dic_info = {"x": self.x, "y": self.y, "snaped": self.snaped}
                    # self.info.err(None,"mapping:",dic_info)
                    # self.info.err(None, "addpoint_attributes:", self.addpoint_attributes)
                    for k, v in self.addpoint_attributes.items():
                        # self.info.err(None,"attribute:",k,"value:",dic_info[v])
                        feat[k] = layer.fields().field(k).convertCompatible(dic_info[v])
                    features = [feat]
                    layer.featureAdded.connect(self.select_new_feature)
                    self.save_features(layer, features)
                    layer.featureAdded.disconnect(self.select_new_feature)

                    self.marker.hide()
                    self.helper.refreshLayer(layer)
                    self.gtomain.runcmd(self.tools_after_addpoint)
                else:
                    self.info.gtoWarning('Ungültige Koordinaten! x:', self.x, "y:", self.y)
            else:
                self.info.gtoWarning('Kein Punktlayer ausgewählt!')
        except Exception as e:
            self.info.err(e)
    def addFeature(self):
        text = self.lineEdit.text().strip()
        if text == "":
            return
        layer = self.iface.activeLayer()
        if layer is None:
            return
        try:
            if (self.inputProjection == 0) or (text[0] == '{'):
                # If this is GeoJson it does not matter what inputProjection is
                if text[0] == '{':  # This may be a GeoJSON point
                    codec = QTextCodec.codecForName("UTF-8")
                    fields = QgsJsonUtils.stringToFields(text, codec)
                    fet = QgsJsonUtils.stringToFeatureList(text, fields, codec)
                    if (len(fet) == 0) or not fet[0].isValid():
                        raise ValueError('Invalid Coordinates')

                    geom = fet[0].geometry()
                    if geom.isEmpty() or (geom.wkbType() != QgsWkbTypes.Point):
                        raise ValueError('Invalid GeoJSON Geometry')
                    pt = geom.asPoint()
                    lat = pt.y()
                    lon = pt.x()
                elif re.search(r'POINT\(', text) is not None:
                    m = re.findall(r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                else:
                    lat, lon = parseDMSString(text, self.inputXYOrder)
                srcCrs = epsg4326
            elif self.inputProjection == 1:
                # This is an MGRS coordinate
                text = re.sub(r'\s+', '', text)  # Remove all white space
                lat, lon = mgrs.toWgs(text)
                srcCrs = epsg4326
            elif self.inputProjection == 4:
                text = text.strip()
                coord = olc.decode(text)
                lat = coord.latitudeCenter
                lon = coord.longitudeCenter
                srcCrs = epsg4326
            elif self.inputProjection == 5:
                text = text.strip()
                pt = utm2Point(text, epsg4326)
                lat = pt.y()
                lon = pt.x()
                srcCrs = epsg4326
            else:  # Is either the project or custom CRS
                if re.search(r'POINT\(', text) is None:
                    coords = re.split(r'[\s,;:]+', text, 1)
                    if len(coords) < 2:
                        raise ValueError('Invalid Coordinates')
                    if self.inputXYOrder == 0:  # Y, X Order
                        lat = float(coords[0])
                        lon = float(coords[1])
                    else:
                        lon = float(coords[0])
                        lat = float(coords[1])
                else:
                    m = re.findall(r'POINT\(\s*([+-]?\d*\.?\d*)\s+([+-]?\d*\.?\d*)', text)
                    if len(m) != 1:
                        raise ValueError('Invalid Coordinates')
                    lon = float(m[0][0])
                    lat = float(m[0][1])
                if self.inputProjection == 2:  # Project CRS
                    srcCrs = self.canvas.mapSettings().destinationCrs()
                else:
                    srcCrs = QgsCoordinateReferenceSystem(self.inputCustomCRS)
        except Exception:
            # traceback.print_exc()
            self.iface.messageBar().pushMessage("", "Invalid Coordinate", level=Qgis.Warning, duration=2)
            return
        self.lineEdit.clear()
        caps = layer.dataProvider().capabilities()
        if caps & QgsVectorDataProvider.AddFeatures:
            destCRS = layer.crs()  # Get the CRS of the layer we are adding a point toWgs
            transform = QgsCoordinateTransform(srcCrs, destCRS, QgsProject.instance())
            # Transform the input coordinate projection to the layer CRS
            x, y = transform.transform(float(lon), float(lat))
            geom = QgsGeometry.fromPointXY(QgsPointXY(x, y))
            feat = QgsVectorLayerUtils.createFeature(layer, geom, {}, layer.createExpressionContext() )
            if layer.fields().count() == 0:
                layer.addFeature(feat)
                self.lltools.zoomTo(srcCrs, lat, lon)
            else:
                if self.iface.openFeatureForm(layer, feat):
                    layer.addFeature(feat)
                    self.lltools.zoomTo(srcCrs, lat, lon)
예제 #24
0
def make_features_compatible(new_features, input_layer):
    """Try to make the new features compatible with old features by:

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

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

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

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

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

        if input_layer_has_geom and new_f_has_geom and \
                new_f.geometry().wkbType() != input_wkb_type:  # Fix geometry
            # Single -> Multi
            if (QgsWkbTypes.isMultiType(input_wkb_type) and not
                    new_f.geometry().isMultipart()):
                new_geom = new_f.geometry()
                new_geom.convertToMultiType()
                new_f.setGeometry(new_geom)
            # Drop Z/M
            if (new_f.geometry().constGet().is3D() and not QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropZValue()
                new_f.setGeometry(new_geom)
            if (new_f.geometry().constGet().isMeasure() and not QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().dropMValue()
                new_f.setGeometry(new_geom)
            # Add Z/M back (set it to 0)
            if (not new_f.geometry().constGet().is3D() and QgsWkbTypes.hasZ(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addZValue(0.0)
                new_f.setGeometry(new_geom)
            if (not new_f.geometry().constGet().isMeasure() and QgsWkbTypes.hasM(input_wkb_type)):
                new_geom = new_f.geometry()
                new_geom.get().addMValue(0.0)
                new_f.setGeometry(new_geom)
            # Multi -> Single
            if (not QgsWkbTypes.isMultiType(input_wkb_type) and
                    new_f.geometry().isMultipart()):
                g = new_f.geometry()
                g2 = g.constGet()
                for i in range(g2.partCount()):
                    # Clone or crash!
                    g4 = QgsGeometry(g2.geometryN(i).clone())
                    f = QgsVectorLayerUtils.createFeature(input_layer, g4, {i: new_f.attribute(i) for i in range(new_f.fields().count())})
                    result_features.append(f)
            else:
                result_features.append(new_f)
        else:
            result_features.append(new_f)
    return result_features
    def processAlgorithm(self, parameters, context, feedback):
        g_import = self.parameterAsSource(parameters, self.INPUT, context)
        g_label = self.parameterAsString(
            parameters,
            self.MANHOLE_NAME_FIELD,
            context
        )
        g_regard = self.parameterAsVectorLayer(
            parameters,
            self.GEOM_MANHOLES,
            context
        )

        # Construction des objets regards
        xform = QgsCoordinateTransform(
            g_import.sourceCrs(),
            g_regard.crs(),
            context.project()
        )
        features = []
        i = 0
        for feat in g_import.getFeatures():
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.MAN_HOLES: 0}

            geometry = feat.geometry()
            if geometry is None:
                # TODO check utility?
                continue

            feat_i = QgsVectorLayerUtils.createFeature(g_regard)
            feat_i.setAttribute('label', feat[g_label])

            geometry.transform(xform)
            feat_i.setGeometry(geometry)
            features.append(feat_i)
            i += 1

        # Stop the algorithm if cancel button has been clicked
        if feedback.isCanceled():
            return {self.MAN_HOLES: 0}

        # Ajout des objets regards
        if features:
            g_regard.startEditing()
            (res, outFeats) = g_regard.dataProvider().addFeatures(features)
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr(
                        '* ERREUR: lors de l\'enregistrement des regards {}'
                    ).format(
                        ', '.join(g_regard.dataProvider().errors())
                    )
                )
            if not g_regard.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit {}.').format(
                        g_regard.commitErrors()
                    )
                )

        feedback.pushInfo('{} manholes have been imported'.format(i))
        return {self.MAN_HOLES: i}
예제 #26
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer(
            "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
            "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

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

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

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

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

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer,
                                              attributes={
                                                  0: 'a',
                                                  2: 6.0
                                              })
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # 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()
예제 #27
0
    def testCreateFeature(self):
        """ test creating a feature respecting defaults and constraints """
        layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double",
                               "addfeat", "memory")
        # add a bunch of features
        f = QgsFeature()
        f.setAttributes(["test", 123, 1.0])
        f1 = QgsFeature(2)
        f1.setAttributes(["test_1", 124, 1.1])
        f2 = QgsFeature(3)
        f2.setAttributes(["test_2", 125, 2.4])
        f3 = QgsFeature(4)
        f3.setAttributes(["test_3", 126, 1.7])
        f4 = QgsFeature(5)
        f4.setAttributes(["superpig", 127, 0.8])
        self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4]))

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

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

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

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

        # layer with default value expression
        layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4'))
        f = QgsVectorLayerUtils.createFeature(layer)
        self.assertEqual(f.attributes(), [NULL, NULL, 12])
        # we do not expect the default value expression to take precedence over the attribute map
        f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0})
        self.assertEqual(f.attributes(), ['a', NULL, 6.0])
        # 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()
예제 #28
0
    def addDroppedDocument(self, fileUrl):

        if self.checkLayerEditingMode() is False:
            return

        # Workaround because of QGIS not resetting this property after linking features
        self.editorContext().vectorLayerTools().setForceSuppressFormPopup(
            False)

        layer = self.relation().referencingLayer()
        if self.nmRelation().isValid():
            layer = self.nmRelation().referencedLayer()

        default_documents_path = str()
        if self.documents_path:
            exp = QgsExpression(self.documents_path)
            context = QgsExpressionContext()
            context.appendScopes(
                QgsExpressionContextUtils.globalProjectLayerScopes(layer))
            default_documents_path = str(exp.evaluate(context))

        filename = QUrl(fileUrl).toLocalFile()
        if default_documents_path:
            filename = QDir(default_documents_path).relativeFilePath(filename)

        keyAttrs = dict()

        # Fields of the linking table
        fields = self.relation().referencingLayer().fields()

        # For generated relations insert the referenced layer field
        if self.relation().type() == QgsRelation.Generated:
            polyRel = self.relation().polymorphicRelation()
            keyAttrs[fields.indexFromName(
                polyRel.referencedLayerField())] = polyRel.layerRepresentation(
                    self.relation().referencedLayer())

        if self.nmRelation().isValid():
            # only normal relations support m:n relation
            if self.nmRelation().type() != QgsRelation.Normal:
                QMessageBox.critical(
                    self, self.tr("Add document"),
                    self.
                    tr("Invalid relation, Only normal relations support m:n relation."
                       ))
                return

            # Pre fill inserting document filepath
            attributes = {
                self.nmRelation().referencedLayer().fields().indexFromName(self.document_filename):
                filename
            }

            # n:m Relation: first let the user create a new feature on the other table
            # and autocreate a new linking feature.
            ok, feature = self.editorContext().vectorLayerTools().addFeature(
                self.nmRelation().referencedLayer(), attributes, QgsGeometry())
            if not ok:
                QMessageBox.critical(
                    self, self.tr("Add document"),
                    self.tr("Could not add a new linking feature."))
                return

            for key in self.relation().fieldPairs():
                keyAttrs[fields.indexOf(key)] = self.feature().attribute(
                    self.relation().fieldPairs()[key])

            for key in self.nmRelation().fieldPairs():
                keyAttrs[fields.indexOf(key)] = feature.attribute(
                    self.nmRelation().fieldPairs()[key])

            linkFeature = QgsVectorLayerUtils.createFeature(
                self.relation().referencingLayer(), QgsGeometry(), keyAttrs,
                self.relation().referencingLayer().createExpressionContext())

            if not self.relation().referencingLayer().addFeature(linkFeature):
                QMessageBox.critical(self, self.tr("Add document"),
                                     self.tr("Could not add a new feature."))
                return

        else:
            for key in self.relation().fieldPairs():
                keyAttrs[fields.indexFromName(key)] = self.feature().attribute(
                    self.relation().fieldPairs()[key])

            # Pre fill inserting document filepath
            keyAttrs[fields] = filename

            ok, feature = self.editorContext().vectorLayerTools().addFeature(
                self.relation().referencingLayer(), keyAttrs, QgsGeometry())
            if not ok:
                QMessageBox.critical(self, self.tr("Add document"),
                                     self.tr("Could not add a new feature."))
                return

        self.updateUi()
    def processAlgorithm(self, parameters, context, feedback):
        path = self.parameterAsFile(parameters, self.INPUT, context)
        t_file = self.parameterAsVectorLayer(
            parameters,
            self.FILE_TABLE,
            context
        )
        t_troncon = self.parameterAsVectorLayer(
            parameters,
            self.SEGMENT_TABLE,
            context
        )
        t_obs = self.parameterAsVectorLayer(
            parameters,
            self.OBSERVATIONS_TABLE,
            context
        )
        t_regard = self.parameterAsVectorLayer(
            parameters,
            self.MANHOLES_TABLE,
            context
        )

        paths = path.split(';')
        if len(paths) != 1:
            raise QgsProcessingException(
                tr('* ERREUR: 1 fichier a la fois {}.').format(path)
            )

        if not os.path.exists(path):
            raise QgsProcessingException(
                tr('* ERREUR: {} n\'existe pas.').format(path)
            )

        # add date fields to itv file table
        # relation_aggregate(
        #     'itv_tronco_id_file_itv_file20_id',
        #     'max',
        #     "abf"
        # )
        if 'date_debut' not in t_file.dataProvider().fields().names():
            with edit(t_file):
                res = t_file.dataProvider().addAttributes([
                    QgsField("date_debut", QVariant.String),
                    QgsField("date_fin", QVariant.String)
                ])
                if res:
                    t_file.updateFields()

        feat_file = None
        with open(path, 'rb') as f:
            basename = os.path.basename(path)
            md = hashlib.md5()
            md.update(f.read())
            hashcontent = md.hexdigest()

            feat_file = QgsVectorLayerUtils.createFeature(t_file)
            feat_file.setAttribute('basename', basename)
            feat_file.setAttribute('hashcontent', hashcontent)

        if not feat_file:
            raise QgsProcessingException(
                tr(
                    '* ERREUR: le fichier {} n\'a pas été lu '
                    'correctement.'
                ).format(path)
            )

        exp_context = QgsExpressionContext()
        exp_context.appendScope(
            QgsExpressionContextUtils.globalScope()
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project())
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_file)
        )

        exp_str = QgsExpression.createFieldEqualityExpression(
            'basename', feat_file['basename']
        ) + ' AND ' + QgsExpression.createFieldEqualityExpression(
            'hashcontent', feat_file['hashcontent']
        )
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()
                )
            )

        request = QgsFeatureRequest(exp, exp_context)
        request.setLimit(1)

        for _t in t_file.getFeatures(request):
            raise QgsProcessingException(
                tr('* ERREUR: le fichier {} a deja ete lu').format(
                    path
                )
            )

        exp_str = QgsExpression.createFieldEqualityExpression(
            'hashcontent', feat_file['hashcontent']
        )
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    (exp.expression(), exp.evalErrorString())
                )
            )

        request = QgsFeatureRequest(exp, exp_context)
        request.setLimit(1)

        for _t in t_file.getFeatures(request):
            raise QgsProcessingException(
                tr(
                    '* ERREUR: le fichier {} semble deja avoir ete lu'
                ).format(path)
            )

        exp_context = QgsExpressionContext()
        exp_context.appendScope(
            QgsExpressionContextUtils.globalScope()
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project())
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_troncon)
        )

        exp_str = 'maximum("id")'
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr(
                    '* ERROR: Expression {} has eval error: {}'
                ).format(exp.expression(), exp.evalErrorString())
            )

        last_t_id = exp.evaluate(exp_context)
        if not last_t_id:
            last_t_id = 0

        exp_context = QgsExpressionContext()
        exp_context.appendScope(
            QgsExpressionContextUtils.globalScope()
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project())
        )
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_regard)
        )

        exp_str = 'maximum("id")'
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr(
                    '* ERROR: Expression {} has eval error: {}'
                ).format(exp.expression(), exp.evalErrorString())
            )

        last_r_id = exp.evaluate(exp_context)
        if not last_r_id:
            last_r_id = 0

        # lecture des entetes
        ENCODING = 'ISO-8859-1'
        LANG = 'fr'
        DELIMITER = ','
        DECIMAL = '.'
        QUOTECHAR = '"'
        VERSION = ''

        with open(path, 'rb') as f:
            for line in f:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}
                try:
                    line = line.decode()
                except UnicodeDecodeError:
                    raise QgsProcessingException(
                        'Error while reading {}'.format(path)
                    )
                # remove break line
                line = line.replace('\n', '').replace('\r', '')
                if line.startswith('#'):
                    if line.startswith('#A'):
                        if line.startswith('#A1'):
                            ENCODING = line[4:]
                            if ENCODING.find(':') != -1:
                                ENCODING = ENCODING[:ENCODING.find(':')]
                        elif line.startswith('#A2'):
                            LANG = line[4:]
                        elif line.startswith('#A3'):
                            DELIMITER = line[4:]
                        elif line.startswith('#A4'):
                            DECIMAL = line[4:]
                        elif line.startswith('#A5'):
                            QUOTECHAR = line[4:]
                    else:
                        break

        # Dialect CSV pour la lecture des tableaux de valeurs du
        # fichier d'ITV
        class itvDialect(csv.Dialect):
            strict = True
            skipinitialspace = True
            quoting = csv.QUOTE_MINIMAL
            delimiter = DELIMITER
            quotechar = QUOTECHAR
            lineterminator = '\r\n'

        # Liste des troncons, observations et regards
        troncons = []
        regards = []
        observations = []

        # Lectures des donnees
        with open(path, 'rb') as f:
            # Identifiant de départ
            t_id = last_t_id
            r_id = last_r_id
            # Entête
            header = []
            # Nom du tableau
            array = ''
            # Observations de troncons ?
            obs_for_troncon = False
            # initialisation du Dialect CSV pour ITV
            dia = itvDialect()
            # Lecture ligne à ligne du fichier
            for line in f:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}
                # Decoding line en utilisant l'encoding du fichier
                line = line.decode(ENCODING)
                # remove break line
                line = line.replace('\n', '').replace('\r', '')
                # Ligne commençant par un # est une ligne d'entête
                if line.startswith('#'):
                    # Entête de troncon ou regard
                    if line.startswith('#B'):
                        l_b = io.StringIO(line[5:])
                        for l_r in csv.reader(l_b, dia):
                            header = l_r
                            break
                        array = line[1:4]
                        continue
                    # Entête d'observation
                    elif line.startswith('#C'):
                        if header[0].startswith('A'):
                            obs_for_troncon = True
                        else:
                            obs_for_troncon = False
                        l_b = io.StringIO(line[3:])
                        for l_r in csv.reader(l_b, dia):
                            header = l_r
                            break
                        array = line[1:2]
                        continue
                    # Fin d'observation
                    elif line.startswith('#Z'):
                        header = []
                        array = ''
                        obs_for_troncon = False
                        continue
                # La ligne contient des donnees
                else:
                    if not header:  # an error in the file structure
                        continue
                    l_b = io.StringIO(line)
                    for l_r in csv.reader(l_b, dia):
                        data = l_r
                        row = list(
                            zip(
                                [h.lower() for h in header],
                                [t for t in data]
                            )
                        )
                        # observation
                        if array == 'C':
                            if obs_for_troncon:
                                observations.append(
                                    row + [('id_troncon', t_id)]
                                )
                        # Premiere ligne de description d'un troncon ou regard
                        elif array == 'B01':
                            if header[0].startswith('A'):
                                t_id += 1
                                troncons.append([('id', t_id)] + row)
                            elif header[0].startswith('C'):
                                r_id += 1
                                regards.append([('id', r_id)] + row)
                        # Ligne complémentaire de description
                        else:
                            if header[0].startswith('A'):
                                troncons[-1] += row
                            elif header[0].startswith('C'):
                                regards[-1] += row

        # Recuperation des references de noeuds et dates
        itv_dates = []
        regard_node_refs = []
        regard_ref_id = {}
        max_r_id = last_r_id
        for reg in regards:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_rg = dict(reg)
            if d_rg['caa'] and d_rg['caa'] not in regard_node_refs:
                regard_node_refs.append(d_rg['caa'])
                regard_ref_id[d_rg['caa']] = d_rg['id']
            if d_rg['id'] and d_rg['id'] > max_r_id:
                max_r_id = d_rg['id']
            if 'cbf' in d_rg and d_rg['cbf'] not in itv_dates:
                itv_dates.append(d_rg['cbf'])

        node_refs = []
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_tr = dict(tro)
            # The nodes ref are stored in AAB, AAD, AAF and AAT
            if 'aab' in d_tr and \
                    d_tr['aab'] and \
                    d_tr['aab'] not in regard_node_refs and \
                    d_tr['aab'] not in node_refs:
                node_refs.append(d_tr['aab'])
            if 'aad' in d_tr and \
                    d_tr['aad'] and \
                    d_tr['aad'] not in regard_node_refs and \
                    d_tr['aad'] not in node_refs:
                node_refs.append(d_tr['aad'])
            if 'aaf' in d_tr and \
                    d_tr['aaf'] and \
                    d_tr['aaf'] not in regard_node_refs and \
                    d_tr['aaf'] not in node_refs:
                node_refs.append(d_tr['aaf'])
            if 'aat' in d_tr and \
                    d_tr['aat'] and \
                    d_tr['aat'] not in regard_node_refs and \
                    d_tr['aat'] not in node_refs:
                node_refs.append(d_tr['aat'])
            if 'abf' in d_tr and d_tr['abf'] not in itv_dates:
                itv_dates.append(d_tr['abf'])

        # Ajout des regards manquant
        for n_ref in node_refs:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            max_r_id += 1
            regards.append([('id', max_r_id), ('caa', n_ref)])
            regard_ref_id[n_ref] = max_r_id

        # Ajout des identifiants de regards aux tronçons
        regard_refs = regard_ref_id.keys()
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            d_tr = dict(tro)
            # If AAD is not defined then it is equal to AAB
            if 'aad' not in d:
                d['aad'] = d['aab']
            if d_tr['aad'] and \
                    d_tr['aad'] in regard_refs:
                tro += [('id_regard1', regard_ref_id[d_tr['aad']])]
            if d_tr['aaf'] and \
                    d_tr['aaf'] in regard_refs:
                tro += [('id_regard2', regard_ref_id[d_tr['aaf']])]
            if 'aat' in d_tr.keys() and \
                    d_tr['aat'] and \
                    d_tr['aat'] in regard_refs:
                tro += [('id_regard3', regard_ref_id[d_tr['aat']])]

        # Verification des champs
        fields = provider_fields(t_troncon.fields())
        for tro in troncons:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in tro:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs de tronçon "{}" est inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs de tronçon "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        fields = provider_fields(t_obs.fields())
        for obs in observations:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in obs:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs d\'observation "{}" est '
                            'inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs d\'observation "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        fields = provider_fields(t_regard.fields())
        for reg in regards:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SUCCESS: 0}

            for key, val in reg:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SUCCESS: 0}

                if fields.indexOf(key) == -1:
                    raise QgsProcessingException(
                        tr(
                            '* ERREUR dans le fichier : '
                            'le champs de regard "{}" est inconnue'
                        ).format(key)
                    )
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        try:
                            float(val.replace(DECIMAL, '.'))
                        except BaseException:
                            raise QgsProcessingException(
                                tr(
                                    '* ERREUR dans le fichier : '
                                    'le champs de regard "{}" est '
                                    'numérique mais pas la valeur "{}"'
                                ).format(key, val)
                            )

        # Finalisation objet fichier
        feat_file.setAttribute('encoding', ENCODING)
        feat_file.setAttribute('lang', LANG)
        if VERSION:
            feat_file.setAttribute('version', VERSION)
        if itv_dates:
            feat_file.setAttribute('date_debut', min(itv_dates))
            feat_file.setAttribute('date_fin', max(itv_dates))

        # Stop the algorithm if cancel button has been clicked
        if feedback.isCanceled():
            return {self.SUCCESS: 0}

        # Ajout de l'objet fichier
        t_file.startEditing()
        (res, outFeats) = t_file.dataProvider().addFeatures([feat_file])
        if not res or not outFeats:
            raise QgsProcessingException(
                tr(
                    '* ERREUR: lors de l\'enregistrement du fichier {}'
                ).format(', '.join(t_file.dataProvider().errors()))
            )
        if not t_file.commitChanges():
            raise QgsProcessingException(
                tr('* ERROR: Commit {}.').format(t_file.commitErrors())
            )

        # Mise a jour de l'identifiant de l'objet fichier
        feat_file.setAttribute('id', outFeats[0]['id'])

        # Creation des objets troncons
        features = []
        fields = provider_fields(t_troncon.fields())
        for tro in troncons:
            feat_t = QgsVectorLayerUtils.createFeature(t_troncon)
            feat_t.setAttribute('id_file', feat_file['id'])
            for key, val in tro:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_t.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_t.setAttribute(key, val)
            features.append(feat_t)

        # Ajout des objets troncons
        if features:
            t_troncon.startEditing()
            (res, outFeats) = t_troncon.dataProvider().addFeatures(features)
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr(
                        '* ERREUR: lors de l\'enregistrement '
                        'des troncon {}'
                    ).format(
                        ', '.join(t_troncon.dataProvider().errors())
                    )
                )
            if not t_troncon.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit {}.').format(
                        t_troncon.commitErrors()
                    )
                )

        # Creation des objets observations
        features = []
        fields = provider_fields(t_obs.fields())
        for obs in observations:
            feat_o = QgsVectorLayerUtils.createFeature(t_obs)
            feat_o.setAttribute('id_file', feat_file['id'])
            for key, val in obs:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_o.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_o.setAttribute(key, val)
            features.append(feat_o)

        # Ajout des objets observations
        if features:
            t_obs.startEditing()
            (res, outFeats) = t_obs.dataProvider().addFeatures(features)
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr(
                        '* ERREUR: lors de l\'enregistrement '
                        'des observations {}'
                    ).format(
                        ', '.join(t_obs.dataProvider().errors())
                    )
                )
            if not t_obs.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit {}.').format(
                        t_obs.commitErrors()
                    )
                )

        # Creation des objets regards
        features = []
        fields = provider_fields(t_regard.fields())
        for reg in regards:
            feat_r = QgsVectorLayerUtils.createFeature(t_regard)
            feat_r.setAttribute('id_file', feat_file['id'])
            for key, val in reg:
                field = fields.field(key)
                if isinstance(val, str) and field.isNumeric():
                    if val:
                        feat_r.setAttribute(
                            key, float(val.replace(DECIMAL, '.'))
                        )
                else:
                    feat_r.setAttribute(key, val)
            features.append(feat_r)

        # Ajout des objets regards
        if features:
            t_regard.startEditing()
            (res, outFeats) = t_regard.dataProvider().addFeatures(
                features
            )
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr(
                        '* ERREUR: lors de l\'enregistrement '
                        'des regards {}'
                    ).format(
                        ', '.join(t_regard.dataProvider().errors())
                    )
                )
            if not t_regard.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit %s.').format(
                        t_regard.commitErrors()
                    )
                )

        # Returns empty dict if no outputs
        return {self.SUCCESS: 1}
예제 #30
0
    def accept_dialog(self):
        input_layer = self.mMapLayerComboBox.currentLayer()
        tolerance = self.dsb_tolerance.value()

        if input_layer is None:
            self.qgis_utils.message_emitted.emit(
                QCoreApplication.translate("ControlledMeasurementDialog",
                                           "First select a point layer!"),
                Qgis.Warning)
            return

        if tolerance <= 0:
            self.qgis_utils.message_emitted.emit(
                QCoreApplication.translate(
                    "ControlledMeasurementDialog",
                    "Set a tolerance greater than zero!"), Qgis.Warning)
            return

        # Run model
        model = QgsApplication.processingRegistry().algorithmById(
            "model:Group_Points")
        if model:
            params = {
                'inputpoints': input_layer.name(),
                'bufferdistance': tolerance,
                'native:multiparttosingleparts_2:output': 'memory:'
            }
            res = processing.run("model:Group_Points", params)
        else:
            self.qgis_utils.message_emitted.emit(
                QCoreApplication.translate(
                    "ControlledMeasurementDialog",
                    "Model Group_Points was not found and cannot be opened!"),
                Qgis.Warning)

        # Create memory layer with average points
        groups = res['native:multiparttosingleparts_2:output']
        if not (type(groups) == QgsVectorLayer and groups.isValid()):
            return

        idx = groups.fields().indexOf(GROUP_ID)
        group_ids = groups.uniqueValues(idx)

        layer = QgsVectorLayer("Point?crs=EPSG:3116", "Average Points",
                               "memory")
        layer.dataProvider().addAttributes([
            QgsField("group_id", QVariant.Int),
            QgsField("count", QVariant.Int),
            QgsField("x_mean", QVariant.Double),
            QgsField("y_mean", QVariant.Double),
            QgsField("x_stdev", QVariant.Double),
            QgsField("y_stdev", QVariant.Double)
        ])
        layer.updateFields()
        new_features = []

        for group_id in group_ids:
            x_mean = 0
            y_mean = 0
            count = 0
            x_list = []
            y_list = []
            for feature in groups.getFeatures('"{}" = {}'.format(
                    GROUP_ID, group_id)):
                current_point = feature.geometry().asPoint()
                x_list.append(current_point.x())
                y_list.append(current_point.y())

            x_mean = statistics.mean(x_list)
            y_mean = statistics.mean(y_list)
            x_stdev = statistics.pstdev(x_list)
            y_stdev = statistics.pstdev(y_list)
            geom = QgsGeometry.fromPointXY(QgsPointXY(x_mean, y_mean))

            new_feature = QgsVectorLayerUtils.createFeature(
                layer, geom, {
                    0: group_id,
                    1: len(x_list),
                    2: x_mean,
                    3: y_mean,
                    4: x_stdev,
                    5: y_stdev
                })
            new_features.append(new_feature)

        layer.dataProvider().addFeatures(new_features)
        QgsProject.instance().addMapLayer(layer)

        self.qgis_utils.message_emitted.emit(
            QCoreApplication.translate(
                "ControlledMeasurementDialog",
                "A new average point layer has been added to the map!"),
            Qgis.Info)
예제 #31
0
def make_features_compatible(new_features, input_layer):
    """Try to make the new features compatible with old features by:

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

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

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

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

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

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

        t_regard = self.parameterAsSource(parameters, self.MANHOLES_TABLE,
                                          context)
        g_regard = self.parameterAsSource(parameters, self.GEOM_MANHOLES,
                                          context)
        t_troncon = self.parameterAsVectorLayer(parameters,
                                                self.SEGMENTS_TABLE, context)
        g_troncon = self.parameterAsVectorLayer(parameters, self.GEOM_SEGMENTS,
                                                context)

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_troncon))

        exp_str = '"id_geom_troncon" IS NULL'
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        r_ids = []  # identifiant des regards
        l_t_f = {}  # lien troncon fichier
        l_f_t = {}  # lien fichier troncons
        troncons = {}
        sorties = {}
        entrees = {}
        for tro in t_troncon.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = tro['id']
            r1 = tro['id_regard1']
            r2 = tro['id_regard2']

            if r1 not in r_ids:
                r_ids.append(r1)
            if r2 not in r_ids:
                r_ids.append(r2)

            fid = tro['id_file']
            l_t_f[segment_number] = fid
            if fid in l_f_t:
                l_f_t[fid] = l_f_t[fid] + [segment_number]
            else:
                l_f_t[fid] = [segment_number]

            troncons[segment_number] = (r1, r2)
            if r1 in sorties:
                sorties[r1] = sorties[r1] + [segment_number]
            else:
                sorties[r1] = [segment_number]
            if r2 in entrees:
                entrees[r2] = entrees[r2] + [segment_number]
            else:
                entrees[r2] = [segment_number]

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(t_regard.createExpressionContextScope())

        exp_str = ('"id_geom_regard" IS NOT NULL '
                   'AND '
                   '"id" IN ({})').format(','.join([str(i)
                                                    for i in r_ids] + ['-1']))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        g_ids = []  # identifiants des géométrie de regards
        l_r_g = {}  # lien regard geometrie
        l_g_r = {}  # lien geometrie regards
        for reg in t_regard.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = reg['id']
            fid = reg['id_file']

            if segment_number in sorties:
                sorties[segment_number] = [
                    trid for trid in sorties[segment_number]
                    if l_t_f[trid] == fid
                ]
            if segment_number in entrees:
                entrees[segment_number] = [
                    trid for trid in entrees[segment_number]
                    if l_t_f[trid] == fid
                ]

            gid = reg['id_geom_regard']
            l_r_g[segment_number] = gid
            if gid not in g_ids:
                g_ids.append(gid)
            if gid in l_g_r:
                l_g_r[gid] = l_g_r[gid] + [segment_number]
            else:
                l_g_r[gid] = [segment_number]

        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(g_regard.createExpressionContextScope())

        exp_str = '"id" IN ({})'.format(','.join([str(i)
                                                  for i in g_ids] + ['-1']))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression {} has eval error: {}').format(
                    exp.expression(), exp.evalErrorString()))

        request = QgsFeatureRequest(exp, exp_context)
        points = {}
        pt_labels = {}
        for reg in g_regard.getFeatures(request):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            segment_number = reg['id']
            points[segment_number] = reg.geometry().asPoint()
            pt_labels[segment_number] = reg['label']

        lines = {}
        for gid in points:
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            if gid not in l_g_r:
                continue

            for rid in l_g_r[gid]:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

                if rid in sorties:
                    for tid in sorties[rid]:
                        # Stop the algorithm if cancel button has been clicked
                        if feedback.isCanceled():
                            return {self.SEGMENT_CREATED: None}

                        if tid in lines:
                            continue
                        if tid not in troncons:
                            continue
                        r2 = troncons[tid][1]
                        if r2 not in l_r_g:
                            continue
                        g2 = l_r_g[r2]
                        if g2 not in points:
                            continue
                        lines[tid] = (gid, g2)

                if rid in entrees:
                    for tid in entrees[rid]:
                        # Stop the algorithm if cancel button has been clicked
                        if feedback.isCanceled():
                            return {self.SEGMENT_CREATED: None}

                        if tid in lines:
                            continue
                        if tid not in troncons:
                            continue
                        r1 = troncons[tid][0]
                        if r1 not in l_r_g:
                            continue
                        g1 = l_r_g[r1]
                        if g1 not in points:
                            continue
                        lines[tid] = (g1, gid)

        # Creation des troncons
        l_t_g = {}  # lien troncon et geometrie troncon
        l_pts_t = {}  # lien points troncons
        features = []  # les objets de geometrie de troncon
        geom_point_keys = []  # liste des clés des points troncons
        for tid, pts in lines.items():
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            exp_context = QgsExpressionContext()
            exp_context.appendScope(QgsExpressionContextUtils.globalScope())
            exp_context.appendScope(
                QgsExpressionContextUtils.projectScope(context.project()))
            exp_context.appendScope(
                QgsExpressionContextUtils.layerScope(g_troncon))

            exp_str = ('"id_geom_regard_amont" = {} AND '
                       '"id_geom_regard_aval" = {}').format(pts[0], pts[1])
            exp = QgsExpression(exp_str)

            exp.prepare(exp_context)
            if exp.hasEvalError():
                raise QgsProcessingException(
                    tr('* ERROR: Expression {} has eval error: {}').format(
                        exp.expression(), exp.evalErrorString()))

            request = QgsFeatureRequest(exp, exp_context)
            request.setLimit(1)
            for tro in g_troncon.getFeatures(request):
                l_t_g[tid] = tro['id']
                continue

            if (pts[0], pts[1]) in geom_point_keys:
                l_pts_t[(pts[0], pts[1])].append(tid)
                continue
            else:
                l_pts_t[(pts[0], pts[1])] = [tid]
                geom_point_keys.append((pts[0], pts[1]))

            feat_t = QgsVectorLayerUtils.createFeature(g_troncon)
            feat_t.setAttribute(
                'label', '{}-{}'.format(pt_labels[pts[0]], pt_labels[pts[1]]))
            feat_t.setAttribute('id_geom_regard_amont', pts[0])
            feat_t.setAttribute('id_geom_regard_aval', pts[1])
            feat_t.setGeometry(
                QgsGeometry.fromPolylineXY([points[pts[0]], points[pts[1]]]))
            features.append(feat_t)

        # Ajout des objets troncons
        if features:
            g_troncon.startEditing()
            (res, outFeats) = g_troncon.dataProvider().addFeatures(features)
            if not res or not outFeats:
                raise QgsProcessingException(
                    tr('* ERREUR: lors de l\'enregistrement '
                       'des regards {}').format(', '.join(
                           g_troncon.dataProvider().errors())))
            if not g_troncon.commitChanges():
                raise QgsProcessingException(
                    tr('* ERROR: Commit {}.').format(g_troncon.commitErrors()))

            for tro in outFeats:
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

                if not tro['id']:
                    continue
                key = (tro['id_geom_regard_amont'], tro['id_geom_regard_aval'])
                if key not in geom_point_keys:
                    continue
                for tid in l_pts_t[(pts[0], pts[1])]:
                    l_t_g[tid] = tro['id']
                    # Stop the algorithm if cancel button has been clicked
                    if feedback.isCanceled():
                        return {self.SEGMENT_CREATED: None}

        for tid, pts in lines.items():
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}

            if tid in l_t_g:
                continue
            exp_context = QgsExpressionContext()
            exp_context.appendScope(QgsExpressionContextUtils.globalScope())
            exp_context.appendScope(
                QgsExpressionContextUtils.projectScope(context.project()))
            exp_context.appendScope(
                QgsExpressionContextUtils.layerScope(g_troncon))

            exp_str = ('"id_geom_regard_amont" = {} AND '
                       '"id_geom_regard_aval" = {}').format(pts[0], pts[1])
            exp = QgsExpression(exp_str)

            exp.prepare(exp_context)
            if exp.hasEvalError():
                raise QgsProcessingException(
                    tr('* ERROR: Expression {} has eval error: {}').format(
                        exp.expression(), exp.evalErrorString()))

            request = QgsFeatureRequest(exp, exp_context)
            request.setLimit(1)
            for tro in g_troncon.getFeatures(request):
                l_t_g[tid] = tro['id']
                # Stop the algorithm if cancel button has been clicked
                if feedback.isCanceled():
                    return {self.SEGMENT_CREATED: None}

        if not l_t_g.keys():
            raise QgsProcessingException(
                tr('* ERREUR: Aucune géométrie de tronçon'))

        # Mise a jour de la table troncon
        exp_context = QgsExpressionContext()
        exp_context.appendScope(QgsExpressionContextUtils.globalScope())
        exp_context.appendScope(
            QgsExpressionContextUtils.projectScope(context.project()))
        exp_context.appendScope(
            QgsExpressionContextUtils.layerScope(t_troncon))

        exp_str = ('"id_geom_troncon" IS NULL AND '
                   'id IN ({})').format(','.join(
                       [str(i) for i in l_t_g.keys()]))
        exp = QgsExpression(exp_str)

        exp.prepare(exp_context)
        if exp.hasEvalError():
            raise QgsProcessingException(
                tr('* ERROR: Expression %s has eval error: %s').format(
                    exp.expression(), exp.evalErrorString()))

        # Mise a jour de la table de tronçon
        request = QgsFeatureRequest(exp, exp_context)
        t_troncon.startEditing()
        segment_number = 0
        for tro in t_troncon.getFeatures(request):
            tro.setAttribute('id_geom_troncon', l_t_g[tro['id']])
            t_troncon.updateFeature(tro)
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                return {self.SEGMENT_CREATED: None}
            segment_number += 1

        if not t_troncon.commitChanges():
            raise QgsProcessingException(
                tr('* ERROR: Commit %s.') % t_troncon.commitErrors())

        # Returns empty dict if no outputs
        return {self.SEGMENT_CREATED: segment_number}
    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])