Exemplo n.º 1
0
    def get_boundary_points_not_covered_by_boundary_nodes(
            self, db, boundary_point_layer, boundary_layer, point_bfs_layer,
            error_layer, id_field):
        dict_uuid_boundary = {
            f[id_field]: f[db.names.T_ILI_TID_F]
            for f in boundary_layer.getFeatures()
        }
        dict_uuid_boundary_point = {
            f[id_field]: f[db.names.T_ILI_TID_F]
            for f in boundary_point_layer.getFeatures()
        }

        tmp_boundary_nodes_layer = processing.run("native:extractvertices", {
            'INPUT': boundary_layer,
            'OUTPUT': 'memory:'
        })['OUTPUT']

        # layer is created with unique vertices
        # It is necessary because 'remove duplicate vertices' processing algorithm does not filter the data as we need them
        boundary_nodes_layer = QgsVectorLayer(
            "Point?crs={}".format(boundary_layer.sourceCrs().authid()),
            'unique boundary nodes', "memory")
        data_provider = boundary_nodes_layer.dataProvider()
        data_provider.addAttributes([QgsField(id_field, QVariant.Int)])
        boundary_nodes_layer.updateFields()

        id_field_idx = tmp_boundary_nodes_layer.fields().indexFromName(
            id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])

        filter_fs = []
        fs = []
        for f in tmp_boundary_nodes_layer.getFeatures(request):
            item = [f[id_field], f.geometry().asWkt()]
            if item not in filter_fs:
                filter_fs.append(item)
                fs.append(f)
        del filter_fs
        boundary_nodes_layer.dataProvider().addFeatures(fs)
        GeometryUtils.create_spatial_index(boundary_nodes_layer)

        # Spatial Join between boundary_points and boundary_nodes
        spatial_join_layer = processing.run(
            "qgis:joinattributesbylocation",
            {
                'INPUT': boundary_point_layer,
                'JOIN': boundary_nodes_layer,
                'PREDICATE': [0],  # Intersects
                'JOIN_FIELDS': [db.names.T_ID_F],
                'METHOD': 0,
                'DISCARD_NONMATCHING': False,
                'PREFIX': '',
                'OUTPUT': 'memory:'
            })['OUTPUT']

        # create dict with layer data
        id_field_idx = boundary_point_layer.fields().indexFromName(id_field)
        request = QgsFeatureRequest().setSubsetOfAttributes([id_field_idx])
        dict_boundary_point = {
            feature[id_field]: feature
            for feature in boundary_point_layer.getFeatures(request)
        }

        exp_point_bfs = '"{}" is not null and "{}" is not null'.format(
            db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F,
            db.names.POINT_BFS_T_LC_BOUNDARY_F)
        list_point_bfs = [{
            'boundary_point_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_POINT_F],
            'boundary_id':
            feature[db.names.POINT_BFS_T_LC_BOUNDARY_F]
        } for feature in point_bfs_layer.getFeatures(exp_point_bfs)]

        spatial_join_boundary_point_boundary_node = [{
            'boundary_point_id':
            feature[id_field],
            'boundary_id':
            feature[id_field + '_2']
        } for feature in spatial_join_layer.getFeatures()]

        boundary_point_without_boundary_node = list()
        no_register_point_bfs = list()
        duplicate_in_point_bfs = list()

        # point_bfs topology check
        for item_sj in spatial_join_boundary_point_boundary_node:
            boundary_point_id = item_sj['boundary_point_id']
            boundary_id = item_sj['boundary_id']

            if boundary_id != NULL:
                if item_sj not in list_point_bfs:
                    no_register_point_bfs.append(
                        (boundary_point_id,
                         boundary_id))  # no registered in point bfs
                elif list_point_bfs.count(item_sj) > 1:
                    duplicate_in_point_bfs.append(
                        (boundary_point_id,
                         boundary_id))  # duplicate in point bfs
            else:
                boundary_point_without_boundary_node.append(
                    boundary_point_id)  # boundary point without boundary node

        features = list()

        # boundary point without boundary node
        if boundary_point_without_boundary_node is not None:
            for item in boundary_point_without_boundary_node:
                boundary_point_id = item  # boundary_point_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        None,
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100301),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100301
                    })
                features.append(new_feature)

        # No registered in point_bfs
        if no_register_point_bfs is not None:
            for error_no_register in set(no_register_point_bfs):
                boundary_point_id = error_no_register[0]  # boundary_point_id
                boundary_id = error_no_register[1]  # boundary_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        dict_uuid_boundary.get(boundary_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100302),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100302
                    })
                features.append(new_feature)

        # Duplicate in point_bfs
        if duplicate_in_point_bfs is not None:
            for error_duplicate in set(duplicate_in_point_bfs):
                boundary_point_id = error_duplicate[0]  # boundary_point_id
                boundary_id = error_duplicate[1]  # boundary_id
                boundary_point_geom = dict_boundary_point[
                    boundary_point_id].geometry()
                new_feature = QgsVectorLayerUtils().createFeature(
                    error_layer, boundary_point_geom, {
                        0:
                        dict_uuid_boundary_point.get(boundary_point_id),
                        1:
                        dict_uuid_boundary.get(boundary_id),
                        2:
                        self.quality_rules_manager.get_error_message(
                            QUALITY_RULE_ERROR_CODE_E100303),
                        3:
                        QUALITY_RULE_ERROR_CODE_E100303
                    })
                features.append(new_feature)

        return features
Exemplo n.º 2
0
    def __prepare_layers(self):
        """
        Get layers from DB and prepare snapped layers for all rules
        """
        self.logger.info(
            __name__,
            QCoreApplication.translate("QualityRuleLayerManager",
                                       "Preparing layers..."))
        # First go for ladm-col layers
        ladm_layers = dict()
        for rule_key, rule_layers_config in self.__quality_rule_layers_config.items(
        ):
            if rule_key in self.__rule_keys:  # Only get selected rules' layers
                for layer_name in rule_layers_config[
                        QUALITY_RULE_LADM_COL_LAYERS]:
                    ladm_layers[layer_name] = None

        self.logger.debug(
            __name__,
            QCoreApplication.translate("QualityRuleLayerManager",
                                       "Getting {} LADM-COL layers...").format(
                                           len(ladm_layers)))
        self.app.core.get_layers(self.__db, ladm_layers, load=True)
        if ladm_layers is None:  # If there are errors with get_layers, ladm_layers is None
            self.logger.critical(
                __name__,
                QCoreApplication.translate(
                    "QualityRuleLayerManager",
                    "Couldn't finish preparing required layers!"))
            return False

        for name, layer in ladm_layers.items():
            if layer.isSpatial():
                GeometryUtils.create_spatial_index(
                    layer)  # To improve performance of quality rules

        # If tolerance > 0, prepare adjusted layers
        #   We create an adjusted_layers dict to override ladm_layers per rule.
        #   For that, we need to read the config and, if not yet calculated,
        #   adjust the layers and store them in temporary cache.

        # {rule_key: {layer_name: layer}}, because each rule might need
        # different adjustments for the same layer, compared to other rules
        adjusted_layers = {rule_key: dict() for rule_key in self.__rule_keys}

        if self.__tolerance:
            self.logger.debug(
                __name__,
                QCoreApplication.translate(
                    "QualityRuleLayerManager",
                    "Tolerance > 0, adjusting layers..."))
            self.__adjusted_layers_cache = dict()  # adjusted_layers_key: layer
            count_rules = 0
            total_rules = len([
                rk for rk in self.__rule_keys
                if rk in self.__quality_rule_layers_config
            ])

            with ProcessWithStatus(
                    QCoreApplication.translate(
                        "QualityRuleLayerManager",
                        "Preparing tolerance on layers...")):
                for rule_key, rule_layers_config in self.__quality_rule_layers_config.items(
                ):
                    if rule_key in self.__rule_keys:  # Only get selected rules' layers
                        count_rules += 1
                        self.logger.status(
                            QCoreApplication.translate(
                                "QualityRuleLayerManager",
                                "Preparing tolerance on layers... ({} out of {})"
                            ).format(count_rules, total_rules))
                        if QUALITY_RULE_ADJUSTED_LAYERS in rule_layers_config:

                            for layer_name, snap_config in rule_layers_config[
                                    QUALITY_RULE_ADJUSTED_LAYERS].items():
                                # Read from config
                                input_name = snap_config[
                                    ADJUSTED_INPUT_LAYER]  # input layer name
                                reference_name = snap_config[
                                    ADJUSTED_REFERENCE_LAYER]  # reference layer name
                                behavior = snap_config[
                                    ADJUSTED_BEHAVIOR] if ADJUSTED_BEHAVIOR in snap_config else None
                                fix = snap_config[
                                    FIX_ADJUSTED_LAYER] if FIX_ADJUSTED_LAYER in snap_config else False
                                add_topological_points = snap_config[
                                    ADJUSTED_TOPOLOGICAL_POINTS] if ADJUSTED_TOPOLOGICAL_POINTS in snap_config else False

                                # Get input and reference layers. Note that they could be adjusted layers and in that
                                # case they would have a composed name (see get_key_for_quality_rule_adjusted_layer())
                                input = self.__adjusted_layers_cache[
                                    input_name] if input_name in self.__adjusted_layers_cache else ladm_layers[
                                        input_name]
                                reference = self.__adjusted_layers_cache[
                                    reference_name] if reference_name in self.__adjusted_layers_cache else ladm_layers[
                                        reference_name]

                                # Try to reuse if already calculated!
                                adjusted_layers_key = get_key_for_quality_rule_adjusted_layer(
                                    input_name, reference_name, fix)
                                if adjusted_layers_key not in self.__adjusted_layers_cache:
                                    self.__adjusted_layers_cache[
                                        adjusted_layers_key] = self.app.core.adjust_layer(
                                            input, reference, self.__tolerance,
                                            behavior, fix,
                                            add_topological_points)

                                adjusted_layers[rule_key][
                                    layer_name] = self.__adjusted_layers_cache[
                                        adjusted_layers_key]

            self.logger.debug(
                __name__,
                QCoreApplication.translate("QualityRuleLayerManager",
                                           "Layers adjusted..."))

        # Now that we have both ladm_layers and adjusted_layers, use them
        # in a single member dict of layers per rule (preserving original LADM-COL layers)
        self.__layers = {
            rule_key: {
                QUALITY_RULE_LAYERS: dict(),
                QUALITY_RULE_LADM_COL_LAYERS: dict()
            }
            for rule_key in self.__rule_keys
        }
        for rule_key, rule_layers_config in self.__quality_rule_layers_config.items(
        ):
            if rule_key in self.__rule_keys:  # Only get selected rules' layers
                for layer_name in rule_layers_config[
                        QUALITY_RULE_LADM_COL_LAYERS]:
                    # Fill both subdicts
                    # In LADM-COL layers we send all original layers
                    self.__layers[rule_key][QUALITY_RULE_LADM_COL_LAYERS][
                        layer_name] = ladm_layers[
                            layer_name] if layer_name in ladm_layers else None

                    # In QR_Layers we store the best layer we have available (preferring adjusted over ladm-col)
                    if layer_name in adjusted_layers[rule_key]:
                        self.__layers[rule_key][QUALITY_RULE_LAYERS][
                            layer_name] = adjusted_layers[rule_key][layer_name]
                    elif layer_name in ladm_layers:
                        self.__layers[rule_key][QUALITY_RULE_LAYERS][
                            layer_name] = ladm_layers[layer_name]

                # Let QRs know if they should switch between dicts looking for original geometries
                self.__layers[rule_key][HAS_ADJUSTED_LAYERS] = bool(
                    self.__tolerance)

        # Register adjusted layers so that Processing can properly find them
        if self.__adjusted_layers_cache:
            load_to_registry = [
                layer for key, layer in self.__adjusted_layers_cache.items()
                if layer is not None
            ]
            self.logger.debug(
                __name__,
                "{} adjusted layers loaded to QGIS registry...".format(
                    len(load_to_registry)))
            QgsProject.instance().addMapLayers(load_to_registry, False)

        return True