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])
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])
def testCreateFeature(self): """ test creating a feature respecting defaults and constraints """ layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # add a bunch of features f = QgsFeature() f.setAttributes(["test", 123, 1.0]) f1 = QgsFeature(2) f1.setAttributes(["test_1", 124, 1.1]) f2 = QgsFeature(3) f2.setAttributes(["test_2", 125, 2.4]) f3 = QgsFeature(4) f3.setAttributes(["test_3", 126, 1.7]) f4 = QgsFeature(5) f4.setAttributes(["superpig", 127, 0.8]) self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4])) # no layer self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid()) # basic tests f = QgsVectorLayerUtils.createFeature(layer) self.assertTrue(f.isValid()) self.assertEqual(f.fields(), layer.fields()) self.assertFalse(f.hasGeometry()) self.assertEqual(f.attributes(), [NULL, NULL, NULL]) # set geometry g = QgsGeometry.fromPoint(QgsPoint(100, 200)) f = QgsVectorLayerUtils.createFeature(layer, g) self.assertTrue(f.hasGeometry()) self.assertEqual(f.geometry().exportToWkt(), g.exportToWkt()) # using attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0}) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression layer.setDefaultValueExpression(2, '3*4') f = QgsVectorLayerUtils.createFeature(layer) self.assertEqual(f.attributes(), [NULL, NULL, 12.0]) # we expect the default value expression to take precedence over the attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0}) self.assertEqual(f.attributes(), ['a', NULL, 12.0]) # layer with default value expression based on geometry layer.setDefaultValueExpression(2, '3*$x') f = QgsVectorLayerUtils.createFeature(layer, g) self.assertEqual(f.attributes(), [NULL, NULL, 300.0]) layer.setDefaultValueExpression(2, None) # test with violated unique constraints layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique) f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) self.assertEqual(f.attributes(), ['test_1', 128, NULL]) layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique) f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) self.assertEqual(f.attributes(), ['test_4', 128, NULL])
def testVectorLayerUtilsCreateFeatureWithProviderDefault(self): vl = QgsVectorLayer('%s table="qgis_test"."someData" sql=' % (self.dbconn), "someData", "postgres") default_clause = 'nextval(\'qgis_test."someData_pk_seq"\'::regclass)' self.assertEqual(vl.dataProvider().defaultValueClause(0), default_clause) # check that provider default clause takes precedence over passed attribute values # this also checks that the inbuilt unique constraint handling is bypassed in the case of a provider default clause f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'}) self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", "'qgis'::text", None, None]) # test take vector layer default value expression overrides postgres provider default clause vl.setDefaultValueExpression(3, "'mappy'") f = QgsVectorLayerUtils.createFeature(vl, attributes={1: 5, 3: 'map'}) self.assertEqual(f.attributes(), [default_clause, 5, "'qgis'::text", 'mappy', None, None])
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)
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)
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)
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()
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
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)
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()
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
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
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
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)
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
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)
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}
def testCreateFeature(self): """ test creating a feature respecting defaults and constraints """ layer = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # add a bunch of features f = QgsFeature() f.setAttributes(["test", 123, 1.0]) f1 = QgsFeature(2) f1.setAttributes(["test_1", 124, 1.1]) f2 = QgsFeature(3) f2.setAttributes(["test_2", 125, 2.4]) f3 = QgsFeature(4) f3.setAttributes(["test_3", 126, 1.7]) f4 = QgsFeature(5) f4.setAttributes(["superpig", 127, 0.8]) self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4])) # no layer self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid()) # basic tests f = QgsVectorLayerUtils.createFeature(layer) self.assertTrue(f.isValid()) self.assertEqual(f.fields(), layer.fields()) self.assertFalse(f.hasGeometry()) self.assertEqual(f.attributes(), [NULL, NULL, NULL]) # set geometry g = QgsGeometry.fromPointXY(QgsPointXY(100, 200)) f = QgsVectorLayerUtils.createFeature(layer, g) self.assertTrue(f.hasGeometry()) self.assertEqual(f.geometry().asWkt(), g.asWkt()) # using attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'a', 2: 6.0 }) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4')) f = QgsVectorLayerUtils.createFeature(layer) self.assertEqual(f.attributes(), [NULL, NULL, 12]) # we do not expect the default value expression to take precedence over the attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'a', 2: 6.0 }) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression based on geometry layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x')) f = QgsVectorLayerUtils.createFeature(layer, g) #adjusted so that input value and output feature are the same self.assertEqual(f.attributes(), [NULL, NULL, 300.0]) layer.setDefaultValueDefinition(2, QgsDefaultValue(None)) # test with violated unique constraints layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128 self.assertEqual(f.attributes(), ['test_1', 128, NULL]) layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique) # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) self.assertEqual(f.attributes(), ['test_4', 128, NULL]) # test with violated unique constraints and default value expression providing unique value layer.setDefaultValueDefinition(1, QgsDefaultValue('130')) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value self.assertEqual(f.attributes(), ['test_4', 130, NULL]) # fallback: test with violated unique constraints and default value expression providing already existing value # add the feature with the default value: self.assertTrue(layer.dataProvider().addFeatures([f])) f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 123 }) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value # and since the default value providing an already existing value (130) it generates a unique value (next int: 131) self.assertEqual(f.attributes(), ['test_5', 131, NULL]) layer.setDefaultValueDefinition(1, QgsDefaultValue(None)) # test with manually correct unique constraint f = QgsVectorLayerUtils.createFeature(layer, attributes={ 0: 'test_1', 1: 132 }) self.assertEqual(f.attributes(), ['test_5', 132, NULL]) """ test creating a feature respecting unique values of postgres provider """ layer = QgsVectorLayer( "Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # init connection string dbconn = 'dbname=\'qgis_test\'' if 'QGIS_PGTEST_DB' in os.environ: dbconn = os.environ['QGIS_PGTEST_DB'] # create a vector layer pg_layer = QgsVectorLayer( '{} table="qgis_test"."authors" sql='.format(dbconn), "authors", "postgres") self.assertTrue(pg_layer.isValid()) # check the default clause default_clause = 'nextval(\'qgis_test.authors_pk_seq\'::regclass)' self.assertEqual(pg_layer.dataProvider().defaultValueClause(0), default_clause) # though default_clause is after the first create not unique (until save), it should fill up all the features with it pg_layer.startEditing() f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) self.assertTrue( QgsVectorLayerUtils.valueExists(pg_layer, 0, default_clause)) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) # if a unique value is passed, use it f = QgsVectorLayerUtils.createFeature(pg_layer, attributes={ 0: 40, 1: NULL }) self.assertEqual(f.attributes(), [40, NULL]) # and if a default value is configured use it as well pg_layer.setDefaultValueDefinition(0, QgsDefaultValue('11*4')) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [44, NULL]) pg_layer.rollBack()
def testCreateFeature(self): """ test creating a feature respecting defaults and constraints """ layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # add a bunch of features f = QgsFeature() f.setAttributes(["test", 123, 1.0]) f1 = QgsFeature(2) f1.setAttributes(["test_1", 124, 1.1]) f2 = QgsFeature(3) f2.setAttributes(["test_2", 125, 2.4]) f3 = QgsFeature(4) f3.setAttributes(["test_3", 126, 1.7]) f4 = QgsFeature(5) f4.setAttributes(["superpig", 127, 0.8]) self.assertTrue(layer.dataProvider().addFeatures([f, f1, f2, f3, f4])) # no layer self.assertFalse(QgsVectorLayerUtils.createFeature(None).isValid()) # basic tests f = QgsVectorLayerUtils.createFeature(layer) self.assertTrue(f.isValid()) self.assertEqual(f.fields(), layer.fields()) self.assertFalse(f.hasGeometry()) self.assertEqual(f.attributes(), [NULL, NULL, NULL]) # set geometry g = QgsGeometry.fromPointXY(QgsPointXY(100, 200)) f = QgsVectorLayerUtils.createFeature(layer, g) self.assertTrue(f.hasGeometry()) self.assertEqual(f.geometry().asWkt(), g.asWkt()) # using attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0}) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression layer.setDefaultValueDefinition(2, QgsDefaultValue('3*4')) f = QgsVectorLayerUtils.createFeature(layer) self.assertEqual(f.attributes(), [NULL, NULL, 12]) # we do not expect the default value expression to take precedence over the attribute map f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'a', 2: 6.0}) self.assertEqual(f.attributes(), ['a', NULL, 6.0]) # layer with default value expression based on geometry layer.setDefaultValueDefinition(2, QgsDefaultValue('3*$x')) f = QgsVectorLayerUtils.createFeature(layer, g) #adjusted so that input value and output feature are the same self.assertEqual(f.attributes(), [NULL, NULL, 300.0]) layer.setDefaultValueDefinition(2, QgsDefaultValue(None)) # test with violated unique constraints layer.setFieldConstraint(1, QgsFieldConstraints.ConstraintUnique) f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and sets to 128 self.assertEqual(f.attributes(), ['test_1', 128, NULL]) layer.setFieldConstraint(0, QgsFieldConstraints.ConstraintUnique) # since field 0 and 1 already have values test_1 and 123, the output must be a new unique value f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) self.assertEqual(f.attributes(), ['test_4', 128, NULL]) # test with violated unique constraints and default value expression providing unique value layer.setDefaultValueDefinition(1, QgsDefaultValue('130')) f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value self.assertEqual(f.attributes(), ['test_4', 130, NULL]) # fallback: test with violated unique constraints and default value expression providing already existing value # add the feature with the default value: self.assertTrue(layer.dataProvider().addFeatures([f])) f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 123}) # since field 1 has Unique Constraint, it ignores value 123 that already has been set and adds the default value # and since the default value providing an already existing value (130) it generates a unique value (next int: 131) self.assertEqual(f.attributes(), ['test_5', 131, NULL]) layer.setDefaultValueDefinition(1, QgsDefaultValue(None)) # test with manually correct unique constraint f = QgsVectorLayerUtils.createFeature(layer, attributes={0: 'test_1', 1: 132}) self.assertEqual(f.attributes(), ['test_5', 132, NULL]) """ test creating a feature respecting unique values of postgres provider """ layer = QgsVectorLayer("Point?field=fldtxt:string&field=fldint:integer&field=flddbl:double", "addfeat", "memory") # init connection string dbconn = 'dbname=\'qgis_test\'' if 'QGIS_PGTEST_DB' in os.environ: dbconn = os.environ['QGIS_PGTEST_DB'] # create a vector layer pg_layer = QgsVectorLayer('{} table="qgis_test"."authors" sql='.format(dbconn), "authors", "postgres") self.assertTrue(pg_layer.isValid()) # check the default clause default_clause = 'nextval(\'qgis_test.authors_pk_seq\'::regclass)' self.assertEqual(pg_layer.dataProvider().defaultValueClause(0), default_clause) # though default_clause is after the first create not unique (until save), it should fill up all the features with it pg_layer.startEditing() f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) self.assertTrue(QgsVectorLayerUtils.valueExists(pg_layer, 0, default_clause)) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [default_clause, NULL]) self.assertTrue(pg_layer.addFeatures([f])) # if a unique value is passed, use it f = QgsVectorLayerUtils.createFeature(pg_layer, attributes={0: 40, 1: NULL}) self.assertEqual(f.attributes(), [40, NULL]) # and if a default value is configured use it as well pg_layer.setDefaultValueDefinition(0, QgsDefaultValue('11*4')) f = QgsVectorLayerUtils.createFeature(pg_layer) self.assertEqual(f.attributes(), [44, NULL]) pg_layer.rollBack()
def 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}
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)
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): 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])