def canvasReleaseEvent(self, e): current_point = self.toMapCoordinates(e.pos()) if e.button() == Qt.LeftButton: self.isEmittingPoint = True self.tmpRubberBand.addPoint(current_point, True) elif e.button() == Qt.RightButton: if self.tmpRubberBand.numberOfVertices() > 2: lineString = self.tmpRubberBand.asGeometry( ) # type:QgsGeometry self.tmpRubberBand.reset(True) self.rubberBand.addGeometry( QgsGeometry.convertToType(lineString, QgsWkbTypes.PolygonGeometry)) self.rubberBand.setColor(QColor(255, 0, 0, 50)) self.rubberBand.show() self.isEmittingPoint = False self.chooseComplete = True
def cloneToMemory(self): curlayer = self.iface.mapCanvas().currentLayer() selectedFeatCount = curlayer.selectedFeatureCount() geo = QgsWkbTypes.displayString( curlayer.wkbType()) # wkbType string name of geometry targetLayer = QgsVectorLayer(geo, self.dlg.lineEdit_2.text(), "memory") targetLayer.setCrs(curlayer.sourceCrs()) QgsProject.instance().addMapLayer(targetLayer, False) root = QgsProject.instance().layerTreeRoot() self.setStyleLayer(targetLayer) if self.dlg.checkBoxAtrib.isChecked(): #copy attributes curlayer_attribute_list = curlayer.fields().toList() targetLayer_attribute_list = [] targetLayerpr = targetLayer.dataProvider() for attrib in curlayer_attribute_list: if targetLayer.fields().lookupField(attrib.name()) == -1: targetLayer_attribute_list.append( QgsField(attrib.name(), attrib.type())) with edit(targetLayer): for attr in targetLayer_attribute_list: if attr.type( ) == 1: # иначе игнорируется поле с типом 1 (bool) attr = QgsField( attr.name(), QVariant.String) # конвертируем bool в string res_add = targetLayer.addAttribute(attr) if not res_add: print(u'Не создано поле {}'.format(attr.name())) targetLayer.updateFields() # for feat in curlayer.selectedFeatures(): # not work more # targetLayer.dataProvider().addFeatures([feat]) # not work more # ИЗ МОДУЛЯ Apend Features To layer ----------------------------------------------- # В старом варианте в QGIS3 при добавлении объектов с отличающимся набором аттрибутов # происходила задержка с выводом сообщений в логи. Что затягивало процесс. mapping = dict() for target_idx in targetLayer.fields().allAttributesList(): target_field = targetLayer.fields().field(target_idx) source_idx = curlayer.fields().indexOf(target_field.name()) if source_idx != -1: mapping[target_idx] = source_idx features = curlayer.selectedFeatures() destType = targetLayer.geometryType() destIsMulti = QgsWkbTypes.isMultiType(targetLayer.wkbType()) new_features = [] for current, in_feature in enumerate(features): attrs = { target_idx: in_feature[source_idx] for target_idx, source_idx in mapping.items() } geom = QgsGeometry() if in_feature.hasGeometry() and targetLayer.isSpatial(): # Convert geometry to match destination layer # Adapted from QGIS qgisapp.cpp, pasteFromClipboard() geom = in_feature.geometry() if destType != QgsWkbTypes.UnknownGeometry: newGeometry = geom.convertToType(destType, destIsMulti) if newGeometry.isNull(): continue geom = newGeometry # Avoid intersection if enabled in digitize settings geom.avoidIntersections( QgsProject.instance().avoidIntersectionsLayers()) new_feature = QgsVectorLayerUtils().createFeature( targetLayer, geom, attrs) new_features.append(new_feature) with edit(targetLayer): res = targetLayer.addFeatures(new_features) # ИЗ МОДУЛЯ Apend Features To layer -----------------------------------------------end root.insertLayer(0, targetLayer) self.iface.messageBar().clearWidgets() self.iface.setActiveLayer(targetLayer) curlayer.selectByIds([]) if res: self.iface.messageBar().pushMessage( u"Выполнено", u"Склонировано {0}/{1} объектов".format( len(new_features), selectedFeatCount), duration=5, level=0)
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) source_fields_parameter = self.parameterAsFields( parameters, self.INPUT_FIELD, context) target = self.parameterAsVectorLayer(parameters, self.OUTPUT, context) target_fields_parameter = self.parameterAsFields( parameters, self.OUTPUT_FIELD, context) action_on_duplicate = self.parameterAsEnum(parameters, self.ACTION_ON_DUPLICATE, context) results = { self.OUTPUT: None, self.APPENDED_COUNT: None, self.UPDATED_COUNT: None, self.SKIPPED_COUNT: None } target_value_dict = dict() source_field_unique_values = '' target_field_unique_values = '' source_field_type = None target_field_type = None if source_fields_parameter: source_field_unique_values = source_fields_parameter[0] source_field_type = source.fields().field( source_field_unique_values).type() if target_fields_parameter: target_field_unique_values = target_fields_parameter[0] target_field_type = target.fields().field( target_field_unique_values).type() if source_field_type != target_field_type: feedback.pushInfo( "\nWARNING: Source and target fields to compare have different field types." ) if source_field_unique_values and target_field_unique_values and action_on_duplicate == self.NO_ACTION: feedback.reportError( "\nWARNING: Since you have chosen source and target fields to compare, you need to choose a valid action to apply on duplicate features before running this algorithm." ) return results if action_on_duplicate != self.NO_ACTION and not ( source_field_unique_values and target_field_unique_values): feedback.reportError( "\nWARNING: Since you have chosen an action on duplicate features, you need to choose both source and target fields for comparing values before running this algorithm." ) return results caps = target.dataProvider().capabilities() if not (caps & QgsVectorDataProvider.AddFeatures): feedback.reportError( "\nWARNING: The target layer does not support appending features to it! Choose another target layer." ) return results if action_on_duplicate == self.UPDATE_EXISTING_FEATURE and not ( caps & QgsVectorDataProvider.ChangeAttributeValues and caps & QgsVectorDataProvider.ChangeGeometries): feedback.reportError( "\nWARNING: The target layer does not support updating its features! Choose another action for duplicate features or choose another target layer." ) return results editable_before = False if target.isEditable(): editable_before = True feedback.reportError( "\nWARNING: You need to close the edit session on layer '{}' before running this algorithm." .format(target.name())) return results # Define a mapping between source and target layer mapping = dict() for target_idx in target.fields().allAttributesList(): target_field = target.fields().field(target_idx) source_idx = source.fields().indexOf(target_field.name()) if source_idx != -1: mapping[target_idx] = source_idx # Build dict of target field values so that we can search easily later {value1: [id1, id2], ...} if target_field_unique_values: for f in target.getFeatures(): if f[target_field_unique_values] in target_value_dict: target_value_dict[f[target_field_unique_values]].append( int(f.id())) else: target_value_dict[f[target_field_unique_values]] = [ int(f.id()) ] # Prepare features for the Copy and Paste results[self.APPENDED_COUNT] = 0 total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() destType = target.geometryType() destIsMulti = QgsWkbTypes.isMultiType(target.wkbType()) new_features = list() updated_features = dict() updated_geometries = dict() updated_features_count = 0 updated_geometries_count = 0 skipped_features_count = 0 # To properly count features that were skipped duplicate_features_set = set( ) # To properly count features that were updated for current, in_feature in enumerate(features): if feedback.isCanceled(): break target_feature_exists = False duplicate_target_value = None # If skip is the action, skip as soon as possible if source_field_unique_values: duplicate_target, duplicate_target_value = self.find_duplicate_value( in_feature[source_field_unique_values], source_field_type, target_value_dict, target_field_type) if duplicate_target: if action_on_duplicate == self.SKIP_FEATURE: request = QgsFeatureRequest( target_value_dict[duplicate_target_value] ) # Get target feature ids request.setFlags(QgsFeatureRequest.NoGeometry) request.setSubsetOfAttributes( []) # Note: this adds a new flag skipped_features_count += len( [f for f in target.getFeatures(request)]) continue target_feature_exists = True attrs = { target_idx: in_feature[source_idx] for target_idx, source_idx in mapping.items() } geom = QgsGeometry() if in_feature.hasGeometry() and target.isSpatial(): # Convert geometry to match destination layer # Adapted from QGIS qgisapp.cpp, pasteFromClipboard() geom = in_feature.geometry() if not geom.isNull(): if destType != QgsWkbTypes.UnknownGeometry: newGeometry = geom.convertToType(destType, destIsMulti) if newGeometry.isNull(): continue # Couldn't convert geom = newGeometry # Avoid intersection if enabled in digitize settings geom.avoidIntersections( QgsProject.instance().avoidIntersectionsLayers()) if target_feature_exists and action_on_duplicate == self.UPDATE_EXISTING_FEATURE: for t_f in target.getFeatures( target_value_dict[duplicate_target_value]): duplicate_features_set.add(t_f.id()) updated_features[t_f.id()] = attrs if target.isSpatial(): updated_geometries[t_f.id()] = geom else: # Append new_feature = QgsVectorLayerUtils().createFeature( target, geom, attrs) new_features.append(new_feature) feedback.setProgress(int(current * total)) # Do the Copy and Paste res_add_features = False try: with edit(target): target.beginEditCommand("Appending/Updating features...") if updated_features: for k, v in updated_features.items(): if target.changeAttributeValues(k, v): updated_features_count += 1 else: feedback.reportError( "\nERROR: Target feature (id={}) couldn't be updated to the following attributes: {}." .format(k, v)) if updated_geometries: for k, v in updated_geometries.items(): if target.changeGeometry(k, v): updated_geometries_count += 1 else: feedback.reportError( "\nERROR: Target feature's geometry (id={}) couldn't be updated." .format(k)) if new_features: res_add_features = target.addFeatures(new_features) target.endEditCommand() except QgsEditError as e: if not editable_before: # Let's close the edit session to prepare for a next run target.rollBack() feedback.reportError( "\nERROR: No features could be appended/updated to/in '{}', because of the following error:\n{}\n" .format(target.name(), repr(e))) return results if action_on_duplicate == self.SKIP_FEATURE: feedback.pushInfo( "\nSKIPPED FEATURES: {} duplicate features were skipped while copying features to '{}'!" .format(skipped_features_count, target.name())) results[self.SKIPPED_COUNT] = skipped_features_count if action_on_duplicate == self.UPDATE_EXISTING_FEATURE: feedback.pushInfo( "\nUPDATED FEATURES: {} out of {} duplicate features were updated while copying features to '{}'!" .format(updated_features_count, len(duplicate_features_set), target.name())) results[self.UPDATED_COUNT] = updated_features_count if not new_features: feedback.pushInfo( "\nFINISHED WITHOUT APPENDED FEATURES: There were no features to append to '{}'." .format(target.name())) else: if res_add_features: feedback.pushInfo( "\nAPPENDED FEATURES: {} out of {} features from input layer were successfully appended to '{}'!" .format(len(new_features), source.featureCount(), target.name())) results[self.APPENDED_COUNT] = len(new_features) else: # TODO do we really need this else message below? feedback.reportError( "\nERROR: The {} features from input layer could not be appended to '{}'. Sometimes this might be due to NOT NULL constraints that are not met." .format(source.featureCount(), target.name())) results[self.OUTPUT] = target return results
def drop_z_from_geom(cls, geom: QgsGeometry, geom_type: QgsWkbTypes): target_type = cls._get_non_z_geom_type(geom_type) type_to_convert = QgsWkbTypes.geometryType(target_type) return geom.convertToType(type_to_convert), target_type
def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT, context) target = self.parameterAsVectorLayer(parameters, self.OUTPUT, context) target.dataProvider().clearErrors() editable_before = False if target.isEditable(): editable_before = True feedback.reportError( "\nWARNING: You need to close the edit session on layer '{}' before running this algorithm." .format(target.name())) return {self.OUTPUT: None} # Define a mapping between source and target layer mapping = dict() for target_idx in target.fields().allAttributesList(): target_field = target.fields().field(target_idx) source_idx = source.fields().indexOf(target_field.name()) if source_idx != -1: mapping[target_idx] = source_idx # Copy and Paste total = 100.0 / source.featureCount() if source.featureCount() else 0 features = source.getFeatures() destType = target.geometryType() destIsMulti = QgsWkbTypes.isMultiType(target.wkbType()) # Check if layer has Z or M values. drop_coordinates = list() add_coordinates = list() if QgsWkbTypes().hasM(source.wkbType()): # In ladm we don't use M values, so drop them if present drop_coordinates.append("M") if not QgsWkbTypes().hasZ(source.wkbType()) and QgsWkbTypes().hasZ( target.wkbType()): add_coordinates.append("Z") if QgsWkbTypes().hasZ( source.wkbType()) and not QgsWkbTypes().hasZ(target.wkbType()): drop_coordinates.append("Z") new_features = [] display_target_geometry = QgsWkbTypes.displayString(target.wkbType()) display_source_geometry = QgsWkbTypes.displayString(source.wkbType()) for current, in_feature in enumerate(features): if feedback.isCanceled(): break attrs = { target_idx: in_feature[source_idx] for target_idx, source_idx in mapping.items() } geom = QgsGeometry() if in_feature.hasGeometry() and target.isSpatial(): # Convert geometry to match destination layer # Adapted from QGIS qgisapp.cpp, pasteFromClipboard() geom = in_feature.geometry() if destType != QgsWkbTypes.UnknownGeometry: newGeometry = geom.convertToType(destType, destIsMulti) if newGeometry.isNull(): feedback.reportError( "\nERROR: Geometry type from the source layer ('{}') could not be converted to '{}'." .format(display_source_geometry, display_target_geometry)) return {self.OUTPUT: None} newGeometry = self.transform_geom(newGeometry, drop_coordinates, add_coordinates) geom = newGeometry # Avoid intersection if enabled in digitize settings geom.avoidIntersections( QgsProject.instance().avoidIntersectionsLayers()) new_feature = QgsVectorLayerUtils().createFeature( target, geom, attrs) new_features.append(new_feature) feedback.setProgress(int(current * total)) try: # This might print error messages... But, hey! That's what we want! res = target.dataProvider().addFeatures(new_features) except QgsEditError as e: if not editable_before: # Let's close the edit session to prepare for a next run target.rollBack() feedback.reportError( "\nERROR: No features could be copied into '{}', because of the following error:\n{}\n" .format(target.name(), repr(e))) return {self.OUTPUT: None} if res[0]: feedback.pushInfo( "\nSUCCESS: {} out of {} features from input layer were successfully copied into '{}'!" .format(len(new_features), source.featureCount(), target.name())) else: if target.dataProvider().hasErrors(): feedback.reportError( "\nERROR: The data could not be copied! Details: {}.". format(target.dataProvider().errors()[0])) else: feedback.reportError( "\nERROR: The data could not be copied! No more details from the provider." ) return {self.OUTPUT: target}