示例#1
0
class QualityRuleManager(QObject, metaclass=SingletonQObject):
    def __init__(self):
        self.logger = Logger()
        self.__quality_rules_data = QualityRuleConfig.get_quality_rules_config(
        )
        self.__translated_strings = TranslatableConfigStrings(
        ).get_translatable_config_strings()
        self._quality_rule_groups = dict()
        self.__quality_rules = dict()

        self.role_registry = RoleRegistry()

        self._initialize_quality_rule_manager()

    def _initialize_quality_rule_manager(self):
        for group_k, group_v in self.__quality_rules_data.items():
            self._quality_rule_groups[group_k] = group_v[QUALITY_GROUP_NAME]

            for rule_k, rule_v in group_v[QUALITY_RULES].items():
                self.__quality_rules[rule_k] = QualityRule(rule_v)
        self.logger.info(
            __name__,
            "{} quality rules registered!".format(len(self.__quality_rules)))

    def get_quality_rule(self, rule_key):
        """
        Returns the QualityRule object corresponding to a rule code.

        :param rule_key: rule key
        :return: QualityRule
        """
        return self.__quality_rules.get(rule_key)

    def get_quality_rule_group_name(self, group_key):
        """
        Returns a quality rule group name.

        :param group_key: Group key
        :return: Group name if the group key is found. Otherwise, None.
        """
        return self._quality_rule_groups.get(group_key)

    def get_quality_rules_by_group(self, enum_group=None):
        """
        Returns all rules in a given group. If no enum_group is given,
        it returns the whole set of rules classified by group.

        :param enum_group:  EnumQualityRule.Point|Line|Polygon|Logic
        :return: dict of rules
        """
        quality_rules_group = dict()
        role_key = self.role_registry.get_active_role()
        role_quality_rules = self.role_registry.get_role_quality_rules(
            role_key)

        if enum_group:
            quality_rules_group = {
                k_rule: v_rule
                for k_rule, v_rule in self.__quality_rules.items()
                if k_rule in enum_group and k_rule in role_quality_rules
            }
        else:
            quality_rules_group[EnumQualityRule.Point] = dict()
            quality_rules_group[EnumQualityRule.Line] = dict()
            quality_rules_group[EnumQualityRule.Polygon] = dict()
            quality_rules_group[EnumQualityRule.Logic] = dict()

            for k_quality_rule, v_quality_rule in self.__quality_rules.items():
                if k_quality_rule in role_quality_rules:
                    if k_quality_rule in EnumQualityRule.Point:
                        quality_rules_group[EnumQualityRule.Point][
                            k_quality_rule] = v_quality_rule
                    elif k_quality_rule in EnumQualityRule.Line:
                        quality_rules_group[EnumQualityRule.Line][
                            k_quality_rule] = v_quality_rule
                    elif k_quality_rule in EnumQualityRule.Polygon:
                        quality_rules_group[EnumQualityRule.Polygon][
                            k_quality_rule] = v_quality_rule
                    elif k_quality_rule in EnumQualityRule.Logic:
                        quality_rules_group[EnumQualityRule.Logic][
                            k_quality_rule] = v_quality_rule

            self.logger.debug(
                __name__, "Quality Rules for role '{}': {}".format(
                    role_key,
                    ", ".join([str(rqr.value) for rqr in role_quality_rules])))

        return quality_rules_group

    def get_error_message(self, error_code):
        return self.__translated_strings.get(error_code)
示例#2
0
class QualityRuleController(QObject):

    open_report_called = pyqtSignal(QualityRuleResultLog)  # log result
    quality_rule_layer_removed = pyqtSignal()
    refresh_error_layer_symbology = pyqtSignal(QgsVectorLayer)
    total_progress_changed = pyqtSignal(int)  # Progress value

    def __init__(self, db):
        QObject.__init__(self)
        self.app = AppInterface()
        self.logger = Logger()
        self.__db = db

        self.__tr_dict = TranslatableConfigStrings(
        ).get_translatable_config_strings()

        # Hierarquical dict of qrs and qr groups
        self.__qrs_tree_data = dict()  # {type: {qr_key1: qr_obj1, ...}, ...}

        # Hierarquical dict of qrs and qr groups with general results
        self.__general_results_tree_data = dict(
        )  # {type: {qr_obj1: qr_results1, ...}, ...}

        # Hierarchical dict of qrs and their corresponding error instances
        # feature1: {uuids, rel_uuids, error_type, nombre_ili_obj, details, values, fixed, exception, geom_fks}
        self.__error_results_data = dict()  # {qr_key1: {t_id1: feature1}}

        self.__qr_results_dir_path = ''  # Dir path where results will be stored
        self.__selected_qrs = list()  # QRs to be validated (at least 1)
        self.__selected_qr = None  # QR selected by the user to show its corresponding errors (exactly 1)

        self.__qr_engine = None  # Once set, we can reuse it
        self.__qrs_results = None  # QualityRulesExecutionResult object

        # To cache layers from QR DB
        self.__error_layer = None
        self.__point_layer = None
        self.__line_layer = None
        self.__polygon_layer = None

        # Cache by t_id (built on demand): {t_id1: 'Error', t_id2: 'Corregido', t_id3: 'Exception'}
        self.__error_state_dict = dict()

    def get_tr_string(self, key):
        return self.__tr_dict.get(key, key)

    def validate_qrs(self):
        if self.__qr_engine is None:
            self.__qr_engine = QualityRuleEngine(self.__db,
                                                 self.__selected_qrs,
                                                 self.app.settings.tolerance,
                                                 self.__qr_results_dir_path)
            self.__qr_engine.progress_changed.connect(
                self.total_progress_changed)
        else:
            self.__qr_engine.initialize(self.__db, self.__selected_qrs,
                                        self.app.settings.tolerance,
                                        self.__qr_results_dir_path)
        #self.__qr_engine.qr_logger.show_message_emitted.connect(self.show_log_quality_message)
        #self.__qr_engine.qr_logger.show_button_emitted.connect(self.show_log_quality_button)
        #self.__qr_engine.qr_logger.set_initial_progress_emitted.connect(self.set_log_quality_initial_progress)
        #self.__qr_engine.qr_logger.set_final_progress_emitted.connect(self.set_log_quality_final_progress)

        use_roads = bool(QSettings().value(
            'Asistente-LADM-COL/quality/use_roads', DEFAULT_USE_ROADS_VALUE,
            bool))
        options = {QR_IGACR3006: {'use_roads': use_roads}}

        res, msg, qrs_res = self.__qr_engine.validate_quality_rules(options)
        if not res:
            return res, msg, None

        self.__qrs_results = qrs_res

        self.__connect_layer_willbedeleted_signals(
        )  # Note: Call it after validate_quality_rules!

        res_u, msg_u, output_qr_dir = QualityErrorDBUtils.get_quality_validation_output_path(
            self.__qr_results_dir_path, self.__qr_engine.get_timestamp())

        if len(self.__selected_qrs) == 1:
            pre_text = QCoreApplication.translate(
                "QualityRules", "The quality rule was checked!")
        else:
            pre_text = QCoreApplication.translate(
                "QualityRules",
                "All the {} quality rules were checked!").format(
                    len(self.__selected_qrs))

        post_text = QCoreApplication.translate(
            "QualityRules",
            "Both a PDF report and a GeoPackage database with errors can be found in <a href='file:///{}'>{}</a>."
        ).format(normalize_local_url(output_qr_dir), output_qr_dir)

        self.logger.success_msg(__name__, "{} {}".format(pre_text, post_text))

        self.__emit_refresh_error_layer_symbology()

        return res, msg, self.__qrs_results

    def __connect_layer_willbedeleted_signals(self):
        """
        Iterate QR DB layers from the layer tree and connect their layerwillberemoved signals.
        If a QR DB layer is removed, we'll react in the GUI.
        """
        group = QualityErrorDBUtils.get_quality_error_group(
            self.__qr_engine.get_timestamp())

        if group:
            for tree_layer in group.findLayers():
                try:
                    tree_layer.layer().willBeDeleted.disconnect(
                        self.quality_rule_layer_removed)
                except:
                    pass
                tree_layer.layer().willBeDeleted.connect(
                    self.quality_rule_layer_removed)

    def disconnect_layer_willberemoved_signals(self):
        group = QualityErrorDBUtils.get_quality_error_group(
            self.__qr_engine.get_timestamp(), False)

        if group:
            for tree_layer in group.findLayers():
                try:
                    tree_layer.layer().willBeDeleted.disconnect(
                        self.quality_rule_layer_removed)
                except:
                    pass

    def get_qr_result(self, qr_key):
        """
        Return the QRExecutionResult object for the given qr_key.

        It first attempts to find it in the __qrs_results dict, but, chances are,
        the whole set of QRs hasn't been validated when this method is called,
        so, as a last resort, we go for the tree_data, which is updated each time
        a QR gets its result.
        """
        if self.__qrs_results is not None:
            return self.__qrs_results.result(qr_key)

        for type, qr_dict in self.__general_results_tree_data.items():
            for k, v in qr_dict.items():
                if k.id() == qr_key:
                    return self.__general_results_tree_data[type][k]

        return None

    def __reset_qrs_results(self):
        # To be used when we are returning to select QRs (i.e., to the initial panel)
        self.__qrs_results = None

    def __get_qrs_per_role_and_models(self):
        return QualityRuleRegistry().get_qrs_per_role_and_models(self.__db)

    def load_tree_data(self, mode):
        """
        Builds a hierarchical dict by qr type: {qr_type1: {qr_key1: qr_obj1, ...}, ...}

        Tree data for panel 1.

        :params mode: Value from EnumQualityRulePanelMode (either VALIDATE or READ).
                      For VALIDATE we load QRs from registry (filtered by role and current db models).
                      For READ we load QRs from the DB itself.
        """
        if mode == EnumQualityRulePanelMode.VALIDATE:
            qrs = self.__get_qrs_per_role_and_models(
            )  # Dict of qr key and qr objects.
        else:
            qrs = dict()  # TODO: Read QRs from the QR DB

        for qr_key, qr_obj in qrs.items():
            type = qr_obj.type()
            if type not in self.__qrs_tree_data:
                self.__qrs_tree_data[type] = {qr_key: qr_obj}
            else:
                self.__qrs_tree_data[type][qr_key] = qr_obj

    def get_qrs_tree_data(self):
        return self.__qrs_tree_data

    def set_qr_dir_path(self, path):
        self.__qr_results_dir_path = path

    def set_selected_qrs(self, selected_qrs):
        # We sort them because the engine needs the QRs sorted for the PDF report
        for type, qr_dict in self.__qrs_tree_data.items():
            for qr_key, qr_obj in qr_dict.items():
                if qr_key in selected_qrs:
                    self.__selected_qrs.append(qr_key)

    def get_selected_qrs(self):
        return self.__selected_qrs

    def __reset_selected_qrs(self):
        # To be used when we are returning to select QRs (i.e., to the initial panel)
        self.__selected_qrs = list()

    def reset_vars_for_general_results_panel(self):
        # Initialize variables when we leave the general results panel
        self.__reset_general_results_tree_data()
        self.__reset_selected_qrs()
        self.__reset_qrs_results()
        self.__reset_layers()

        # Call it before removing QR DB group to avoid triggering parent.layer_removed() slot again.
        self.disconnect_layer_willberemoved_signals()

        # When we leave the GRP, we remove the QR DB group from layer tree,
        # because we won't be working anymore with that QR DB
        QualityErrorDBUtils.remove_quality_error_group(
            self.__qr_engine.get_timestamp())

    def reset_vars_for_error_results_panel(self):
        # Initialize variables when we leave the error results panel
        self.__reset_error_results_data()
        self.__reset_selected_qr()
        self.__reset_error_state_dict()
        self.__reset_layers()

    def load_general_results_tree_data(self):
        """
        Builds a hierarchical dict by qr type: {type: {qr_obj1: qr_results1, ...}, ...}

        Tree data for panel 2.
        """
        for type, qr_dict in self.__qrs_tree_data.items():
            for qr_key, qr_obj in qr_dict.items():
                if qr_key in self.__selected_qrs:
                    if type not in self.__general_results_tree_data:
                        self.__general_results_tree_data[type] = {qr_obj: None}
                    else:
                        self.__general_results_tree_data[type][qr_obj] = None

    def get_general_results_tree_data(self):
        return self.__general_results_tree_data

    def __reset_general_results_tree_data(self):
        # To be used when we are returning to select QRs (i.e., to the initial panel)
        self.__general_results_tree_data = dict()

    def set_qr_validation_result(self, qr, qr_result):
        """
        When a QR has its validation result after validation,
        we can store it in our custom dict by using this method.
        """
        for type, qr_dict in self.__general_results_tree_data.items():
            for k, v in qr_dict.items():
                if k == qr:
                    self.__general_results_tree_data[type][k] = qr_result

    def open_report(self):
        if self.__qr_engine:
            log_result = self.__qr_engine.qr_logger.get_log_result()
            self.open_report_called.emit(log_result)

    def set_selected_qr(self, qr_key):
        self.__selected_qr = QualityRuleRegistry().get_quality_rule(qr_key)
        return self.__selected_qr is not None  # We should not be able to continue if we don't find the QR

    def get_selected_qr(self):
        return self.__selected_qr

    def load_error_results_data(self):
        """
        Go to table and bring data to the dict.
        We should keep this dict updated with changes from the user.
        From time to time we reflect this dict changes in the original data source.
        """
        db = self.__qr_engine.get_db_quality()
        names = db.names

        layers = {names.ERR_QUALITY_ERROR_T: None, names.ERR_RULE_TYPE_T: None}
        self.app.core.get_layers(db, layers, load=False)
        if not layers:
            self.logger.critical(
                __name__,
                "Quality error layers ('{}') not found!".format(",".join(
                    list(layers.keys()))))
            return

        # First go for the selected quality error's t_id
        features = LADMData.get_features_from_t_ids(
            layers[names.ERR_RULE_TYPE_T], names.ERR_RULE_TYPE_T_CODE_F,
            [self.__selected_qr.id()])
        t_id = features[0][names.T_ID_F] if features else None
        if not t_id:
            self.logger.critical(
                __name__, "Quality error rule ('{}') not found!".format(
                    self.__selected_qr.id()))
            return

        # Now go for all features that match the selected quality rule
        features = LADMData.get_features_from_t_ids(
            layers[names.ERR_QUALITY_ERROR_T],
            names.ERR_QUALITY_ERROR_T_RULE_TYPE_F, [t_id])

        self.__error_results_data[self.__selected_qr.id()] = {
            feature[names.T_ID_F]: feature
            for feature in features
        }

    def get_error_results_data(self):
        # Get the subdict {t_id1: feature1, ...} corresponding to selected qr
        return self.__error_results_data.get(
            self.__selected_qr.id() if self.__selected_qr else '', dict())

    def __reset_error_results_data(self):
        # To be used when we are returning to select QR results (i.e., to the general results panel)
        self.__error_results_data = dict()

    def error_t_id(self, feature):
        return feature[self.__qr_engine.get_db_quality().names.T_ID_F]

    def is_fixed_error(self, feature):
        db = self.__qr_engine.get_db_quality()
        state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F]
        return self.__get_error_state_value(
            state_t_id) == LADMNames.ERR_ERROR_STATE_D_FIXED_V

    def is_error(self, feature):
        db = self.__qr_engine.get_db_quality()
        state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F]
        return self.__get_error_state_value(
            state_t_id) == LADMNames.ERR_ERROR_STATE_D_ERROR_V

    def is_exception(self, feature):
        db = self.__qr_engine.get_db_quality()
        state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F]
        return self.__get_error_state_value(
            state_t_id) == LADMNames.ERR_ERROR_STATE_D_EXCEPTION_V

    def uuid_objs(self, feature):
        return "\n".join(feature[self.__qr_engine.get_db_quality().names.
                                 ERR_QUALITY_ERROR_T_OBJECT_IDS_F])

    def ili_obj_name(self, feature):
        ili_name = feature[self.__qr_engine.get_db_quality().names.
                           ERR_QUALITY_ERROR_T_ILI_NAME_F]
        return ili_name.split(".")[-1] if ili_name else ''

    def error_type_code_and_display(self, feature):
        db = self.__qr_engine.get_db_quality()
        names = db.names
        layer = self.app.core.get_layer(db, names.ERR_ERROR_TYPE_T, load=False)
        features = LADMData.get_features_from_t_ids(
            layer, names.T_ID_F,
            [feature[db.names.ERR_QUALITY_ERROR_T_ERROR_TYPE_F]])  # tid

        return features[0][
            names.
            ERR_ERROR_TYPE_T_CODE_F] if features else QCoreApplication.translate(
                "QualityRules", "No error type found!"
            ), features[0][
                names.
                ERR_ERROR_TYPE_T_DESCRIPTION_F] if features else QCoreApplication.translate(
                    "QualityRules", "No error description found!")

    def error_details_and_values(self, feature):
        res = ""
        db = self.__qr_engine.get_db_quality()
        details = feature[db.names.ERR_QUALITY_ERROR_T_DETAILS_F]
        values = feature[db.names.ERR_QUALITY_ERROR_T_VALUES_F]

        if details:
            res = details
        if values:
            try:
                res_values = json.loads(values)
                if type(res_values) is dict:
                    items = ""
                    for k, v in res_values.items():
                        items = res + "{}: {}\n".format(k, v)

                    res_values = items.strip()
                else:
                    res_values = str(res_values)
            except json.decoder.JSONDecodeError as e:
                res_values = values

            res = res_values if not res else "{}\n\n{}".format(res, res_values)

        return res

    def error_state(self, feature):
        db = self.__qr_engine.get_db_quality()
        state_t_id = feature[db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F]
        return self.__get_error_state_value(state_t_id)

    def __get_error_state_value(self, state_t_id):
        if state_t_id not in self.__error_state_dict:
            db = self.__qr_engine.get_db_quality()
            self.__error_state_dict[state_t_id] = LADMData(
            ).get_domain_value_from_code(db, db.names.ERR_ERROR_STATE_D,
                                         state_t_id)

        return self.__error_state_dict.get(state_t_id, "")

    def __get_error_state_t_id(self, state_value):
        # Use __error_state_dict to read cached values, but this time we have the value,
        # not the key, so check in dict values and if not found, go for its t_id
        if state_value not in self.__error_state_dict.values():
            db = self.__qr_engine.get_db_quality()
            t_id = LADMData().get_domain_code_from_value(
                db, db.names.ERR_ERROR_STATE_D, state_value)
            self.__error_state_dict[t_id] = state_value

        # Get key by value in a dict:
        return next((k for k in self.__error_state_dict
                     if self.__error_state_dict[k] == state_value), None)

    def __get_error_layer(self):
        if not self.__error_layer:
            db = self.__qr_engine.get_db_quality()
            self.__error_layer = self.app.core.get_layer(
                db, db.names.ERR_QUALITY_ERROR_T)

        return self.__error_layer

    def __get_point_error_layer(self):
        if not self.__point_layer:
            db = self.__qr_engine.get_db_quality()
            self.__point_layer = self.app.core.get_layer(
                db, db.names.ERR_POINT_T)

        return self.__point_layer

    def __get_line_error_layer(self):
        if not self.__line_layer:
            db = self.__qr_engine.get_db_quality()
            self.__line_layer = self.app.core.get_layer(
                db, db.names.ERR_LINE_T)

        return self.__line_layer

    def __get_polygon_error_layer(self):
        if not self.__polygon_layer:
            db = self.__qr_engine.get_db_quality()
            self.__polygon_layer = self.app.core.get_layer(
                db, db.names.ERR_POLYGON_T)

        return self.__polygon_layer

    def __reset_layers(self):
        # To be used when we are returning to select QR results (i.e., to the general results panel)
        self.__error_layer = None
        self.__point_layer = None
        self.__line_layer = None
        self.__polygon_layer = None

    def __reset_selected_qr(self):
        # To be used when we are returning to select QR results (i.e., to the general results panel)
        self.__selected_qr = None

    def __reset_error_state_dict(self):
        # To be used when we are returning to select QR results (i.e., to the general results panel)
        self.__error_state_dict = dict()

    def __error_related_geometries(self, error_t_ids):
        # Prefered geometry types are polygons, lines, points, in that order
        db = self.__qr_engine.get_db_quality()
        error_data = self.get_error_results_data()
        dict_layer_fids = dict()

        for error_t_id in error_t_ids:
            feature = error_data.get(error_t_id, None)

            if feature:
                polygon = feature[db.names.ERR_QUALITY_ERROR_T_POLYGON_F]
                line = feature[db.names.ERR_QUALITY_ERROR_T_LINE_F]
                point = feature[db.names.ERR_QUALITY_ERROR_T_POINT_F]

                if polygon:
                    if 'polygon' in dict_layer_fids:
                        dict_layer_fids['polygon']['fids'].append(polygon)
                    else:
                        dict_layer_fids['polygon'] = {
                            'layer': self.__get_polygon_error_layer(),
                            'fids': [polygon]
                        }
                elif line:
                    if 'line' in dict_layer_fids:
                        dict_layer_fids['line']['fids'].append(line)
                    else:
                        dict_layer_fids['line'] = {
                            'layer': self.__get_line_error_layer(),
                            'fids': [line]
                        }
                elif point:
                    if 'point' in dict_layer_fids:
                        dict_layer_fids['point']['fids'].append(point)
                    else:
                        dict_layer_fids['point'] = {
                            'layer': self.__get_point_error_layer(),
                            'fids': [point]
                        }

        return dict_layer_fids

    def highlight_geometries(self, t_ids):
        res_geometries = self.__error_related_geometries(t_ids)

        if res_geometries:
            # First zoom to geometries
            if len(res_geometries) == 1:  # Only one geometry type related
                for geom_type, dict_layer_fids in res_geometries.items(
                ):  # We know this will be called just once
                    self.app.gui.zoom_to_feature_ids(dict_layer_fids['layer'],
                                                     dict_layer_fids['fids'])
            else:  # Multiple geometry types were found, so combine the extents and then zoom to it
                combined_extent = QgsRectangle()
                for geom_type, dict_layer_fids in res_geometries.items():
                    combined_extent.combineExtentWith(
                        self.app.core.get_extent_from_feature_ids(
                            dict_layer_fids['layer'], dict_layer_fids['fids']))

                self.app.gui.zoom_to_extent(combined_extent)

            # Now highlight geometries
            for geom_type, dict_layer_fids in res_geometries.items():
                self.app.gui.flash_features(dict_layer_fids['layer'],
                                            dict_layer_fids['fids'],
                                            flashes=5)

    def get_uuids_display_name(self):
        names = self.__qr_engine.get_db_quality().names
        res = self.__selected_qr.field_mapping(names).get(
            names.ERR_QUALITY_ERROR_T_OBJECT_IDS_F, '')

        return res if res else QCoreApplication.translate(
            "QualityRules", "UUIDs")

    def set_fixed_error(self, error_t_id, fixed):
        # Save to the intermediate dict of data and to the underlying data source whether an error is fixed or not
        db = self.__qr_engine.get_db_quality()
        idx_state = self.__get_error_layer().fields().indexOf(
            db.names.ERR_QUALITY_ERROR_T_ERROR_STATE_F)

        value = LADMNames.ERR_ERROR_STATE_D_FIXED_V if fixed else LADMNames.ERR_ERROR_STATE_D_ERROR_V
        fixed_or_error_t_id = self.__get_error_state_t_id(value)

        if fixed_or_error_t_id is None:
            self.logger.critical(
                __name__,
                "The error state t_id couldn't be found for value '{}'!".
                format(value))
            return

        # Save to dict
        self.get_error_results_data()[error_t_id].setAttribute(
            idx_state, fixed_or_error_t_id)

        fids = LADMData.get_fids_from_key_values(self.__get_error_layer(),
                                                 db.names.T_ID_F, [error_t_id])

        # Save to underlying data source
        if fids:
            res = self.__get_error_layer().dataProvider(
            ).changeAttributeValues(
                {fids[0]: {
                     idx_state: fixed_or_error_t_id
                 }})

            if not res:
                self.logger.critical(__name__,
                                     "Error modifying the error state value!")
        else:
            self.logger.critical(
                __name__, "Error with t_id '' not found!".format(error_t_id))

    def __emit_refresh_error_layer_symbology(self):
        if self.__get_point_error_layer().featureCount():
            self.refresh_error_layer_symbology.emit(
                self.__get_point_error_layer())

        if self.__get_line_error_layer().featureCount():
            self.refresh_error_layer_symbology.emit(
                self.__get_line_error_layer())

        if self.__get_polygon_error_layer().featureCount():
            self.refresh_error_layer_symbology.emit(
                self.__get_polygon_error_layer())