Ejemplo n.º 1
0
 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
Ejemplo n.º 4
0
 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}