Exemplo n.º 1
0
    def save_current_keywords(self):
        """Save keywords to the layer.

        It will write out the keywords for the current layer.
        This method is based on the KeywordsDialog class.
        """
        current_keywords = self.get_keywords()
        try:
            self.keyword_io.write_keywords(
                layer=self.layer, keywords=current_keywords)
        except InaSAFEError as e:
            error_message = get_error_message(e)
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QMessageBox.warning(
                self,
                tr('InaSAFE'),
                tr('An error was encountered when saving the following '
                   'keywords:\n {error_message}').format(
                    error_message=error_message.to_html()))
        if self.dock is not None:
            # noinspection PyUnresolvedReferences
            self.dock.get_layers()

        # Save default value to QSetting
        if current_keywords.get('inasafe_default_values'):
            for key, value in (
                    list(current_keywords['inasafe_default_values'].items())):
                set_inasafe_default_value_qsetting(
                    self.setting, RECENT, key, value)
Exemplo n.º 2
0
    def save_current_keywords(self):
        """Save keywords to the layer.

        It will write out the keywords for the current layer.
        This method is based on the KeywordsDialog class.
        """
        current_keywords = self.get_keywords()
        try:
            self.keyword_io.write_keywords(layer=self.layer,
                                           keywords=current_keywords)
        except InaSAFEError as e:
            error_message = get_error_message(e)
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QMessageBox.warning(
                self, tr('InaSAFE'),
                tr('An error was encountered when saving the following '
                   'keywords:\n {error_message}').format(
                       error_message=error_message.to_html()))
        if self.dock is not None:
            # noinspection PyUnresolvedReferences
            self.dock.get_layers()

        # Save default value to QSetting
        if current_keywords.get('inasafe_default_values'):
            for key, value in (list(
                    current_keywords['inasafe_default_values'].items())):
                set_inasafe_default_value_qsetting(self.setting, RECENT, key,
                                                   value)
Exemplo n.º 3
0
 def save_default_values(self):
     """Save InaSAFE default values."""
     for parameter_container in self.default_value_parameter_containers:
         parameters = parameter_container.get_parameters()
         for parameter in parameters:
             set_inasafe_default_value_qsetting(self.settings, GLOBAL,
                                                parameter.guid,
                                                parameter.value)
Exemplo n.º 4
0
 def save_default_values(self):
     """Save InaSAFE default values."""
     for parameter_container in self.default_value_parameter_containers:
         parameters = parameter_container.get_parameters()
         for parameter in parameters:
             set_inasafe_default_value_qsetting(
                 self.settings,
                 GLOBAL,
                 parameter.guid,
                 parameter.value
             )
Exemplo n.º 5
0
    def test_inasafe_default_value_qsetting(self):
        """Test for set and get inasafe_default_value_qsetting."""
        # Make sure it's empty
        self.qsetting.clear()

        female_ratio_key = 'female_ratio'
        real_value = get_inasafe_default_value_qsetting(
            self.qsetting, RECENT, female_ratio_key)
        self.assertEqual(zero_default_value, real_value)

        female_ratio_value = 0.8
        set_inasafe_default_value_qsetting(
            self.qsetting, RECENT, female_ratio_key, female_ratio_value)
        real_value = get_inasafe_default_value_qsetting(
            self.qsetting, RECENT, female_ratio_key)
        self.assertEqual(female_ratio_value, real_value)
Exemplo n.º 6
0
    def test_inasafe_default_value_qsetting(self):
        """Test for set and get inasafe_default_value_qsetting."""
        # Make sure it's empty
        self.qsetting.clear()

        female_ratio_key = 'female_ratio'
        real_value = get_inasafe_default_value_qsetting(
            self.qsetting, RECENT, female_ratio_key)
        self.assertEqual(zero_default_value, real_value)

        female_ratio_value = 0.8
        set_inasafe_default_value_qsetting(self.qsetting, RECENT,
                                           female_ratio_key,
                                           female_ratio_value)
        real_value = get_inasafe_default_value_qsetting(
            self.qsetting, RECENT, female_ratio_key)
        self.assertEqual(female_ratio_value, real_value)
Exemplo n.º 7
0
    def save_metadata(self):
        """Save metadata based on the field mapping state."""
        metadata = self.field_mapping_widget.get_field_mapping()
        for key, value in list(metadata['fields'].items()):
            # Delete the key if it's set to None
            if key in self.metadata['inasafe_default_values']:
                self.metadata['inasafe_default_values'].pop(key)
            if value is None or value == []:
                if key in self.metadata['inasafe_fields']:
                    self.metadata['inasafe_fields'].pop(key)
            else:
                self.metadata['inasafe_fields'][key] = value

        for key, value in list(metadata['values'].items()):
            # Delete the key if it's set to None
            if key in self.metadata['inasafe_fields']:
                self.metadata['inasafe_fields'].pop(key)
            if value is None:
                if key in self.metadata['inasafe_default_values']:
                    self.metadata['inasafe_default_values'].pop(key)
            else:
                self.metadata['inasafe_default_values'][key] = value

        # Save metadata
        try:
            self.keyword_io.write_keywords(
                layer=self.layer, keywords=self.metadata)
        except InaSAFEError as e:
            error_message = get_error_message(e)
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QMessageBox.warning(
                self, self.tr('InaSAFE'),
                ((self.tr(
                    'An error was encountered when saving the following '
                    'keywords:\n %s') % error_message.to_html())))

        # Update setting fir recent value
        if self.metadata.get('inasafe_default_values'):
            for key, value in \
                    list(self.metadata['inasafe_default_values'].items()):
                set_inasafe_default_value_qsetting(
                    self.setting, key, RECENT, value)
Exemplo n.º 8
0
class WizardDialog(QDialog, FORM_CLASS):
    """Dialog implementation class for the InaSAFE wizard."""

    resized = pyqtSignal()

    def __init__(self, parent=None, iface=None, dock=None):
        """Constructor for the dialog.

        .. note:: In QtDesigner the advanced editor's predefined keywords
           list should be shown in english always, so when adding entries to
           cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick
           the :safe_qgis:`translatable` property.

        :param parent: Parent widget of this dialog.
        :type parent: QWidget

        :param iface: QGIS QGisAppInterface instance.
        :type iface: QGisAppInterface

        :param dock: Dock widget instance that we can notify of changes to
            the keywords. Optional.
        :type dock: Dock
        """
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle('InaSAFE')
        # Constants
        self.keyword_creation_wizard_name = tr(
            'InaSAFE Keywords Creation Wizard')
        self.ifcw_name = tr('InaSAFE Impact Function Centric Wizard')
        # Note the keys should remain untranslated as we need to write
        # english to the keywords file.
        # Save reference to the QGIS interface and parent
        self.iface = iface
        self.parent = parent
        self.dock = dock
        self.suppress_warning_dialog = False
        self.lblStep.clear()
        # Set icons
        self.lblMainIcon.setPixmap(
            QPixmap(resources_path('img', 'icons', 'icon-white.svg')))

        self.keyword_io = KeywordIO()

        self.is_selected_layer_keywordless = False
        self.parent_step = None

        self.pbnBack.setEnabled(False)
        self.pbnNext.setEnabled(False)
        self.pbnCancel.released.connect(self.reject)

        # Initialize attributes
        self.existing_keywords = None
        self.layer = None
        self.hazard_layer = None
        self.exposure_layer = None
        self.aggregation_layer = None

        self.step_kw_purpose = StepKwPurpose(self)
        self.step_kw_subcategory = StepKwSubcategory(self)
        self.step_kw_hazard_category = StepKwHazardCategory(self)
        self.step_kw_band_selector = StepKwBandSelector(self)
        self.step_kw_layermode = StepKwLayerMode(self)
        self.step_kw_unit = StepKwUnit(self)
        self.step_kw_classification = StepKwClassification(self)
        self.step_kw_field = StepKwField(self)
        self.step_kw_multi_classifications = StepKwMultiClassifications(self)
        self.step_kw_classify = StepKwClassify(self)
        self.step_kw_threshold = StepKwThreshold(self)
        self.step_kw_fields_mapping = StepKwFieldsMapping(self)
        self.step_kw_inasafe_fields = StepKwInaSAFEFields(self)
        self.step_kw_default_inasafe_fields = StepKwDefaultInaSAFEFields(self)
        self.step_kw_inasafe_raster_default_values = \
            StepKwInaSAFERasterDefaultValues(self)
        self.step_kw_source = StepKwSource(self)
        self.step_kw_title = StepKwTitle(self)
        self.step_kw_summary = StepKwSummary(self)

        self.step_fc_functions1 = StepFcFunctions1(self)
        self.step_fc_functions2 = StepFcFunctions2(self)
        self.step_fc_hazlayer_origin = StepFcHazLayerOrigin(self)
        self.step_fc_hazlayer_from_canvas = StepFcHazLayerFromCanvas(self)
        self.step_fc_hazlayer_from_browser = StepFcHazLayerFromBrowser(self)
        self.step_fc_explayer_origin = StepFcExpLayerOrigin(self)
        self.step_fc_explayer_from_canvas = StepFcExpLayerFromCanvas(self)
        self.step_fc_explayer_from_browser = StepFcExpLayerFromBrowser(self)
        self.step_fc_disjoint_layers = StepFcDisjointLayers(self)
        self.step_fc_agglayer_origin = StepFcAggLayerOrigin(self)
        self.step_fc_agglayer_from_canvas = StepFcAggLayerFromCanvas(self)
        self.step_fc_agglayer_from_browser = StepFcAggLayerFromBrowser(self)
        self.step_fc_agglayer_disjoint = StepFcAggLayerDisjoint(self)
        self.step_fc_extent = StepFcExtent(self)
        self.step_fc_extent_disjoint = StepFcExtentDisjoint(self)
        self.step_fc_summary = StepFcSummary(self)
        self.step_fc_analysis = StepFcAnalysis(self)

        self.wizard_help = WizardHelp(self)

        self.stackedWidget.addWidget(self.step_kw_purpose)
        self.stackedWidget.addWidget(self.step_kw_subcategory)
        self.stackedWidget.addWidget(self.step_kw_hazard_category)
        self.stackedWidget.addWidget(self.step_kw_band_selector)
        self.stackedWidget.addWidget(self.step_kw_layermode)
        self.stackedWidget.addWidget(self.step_kw_unit)
        self.stackedWidget.addWidget(self.step_kw_classification)
        self.stackedWidget.addWidget(self.step_kw_field)
        self.stackedWidget.addWidget(self.step_kw_multi_classifications)
        self.stackedWidget.addWidget(self.step_kw_classify)
        self.stackedWidget.addWidget(self.step_kw_threshold)
        self.stackedWidget.addWidget(self.step_kw_fields_mapping)
        self.stackedWidget.addWidget(self.step_kw_inasafe_fields)
        self.stackedWidget.addWidget(self.step_kw_default_inasafe_fields)
        self.stackedWidget.addWidget(
            self.step_kw_inasafe_raster_default_values)
        self.stackedWidget.addWidget(self.step_kw_source)
        self.stackedWidget.addWidget(self.step_kw_title)
        self.stackedWidget.addWidget(self.step_kw_summary)
        self.stackedWidget.addWidget(self.step_fc_functions1)
        self.stackedWidget.addWidget(self.step_fc_functions2)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_origin)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_hazlayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_explayer_origin)
        self.stackedWidget.addWidget(self.step_fc_explayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_explayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_disjoint_layers)
        self.stackedWidget.addWidget(self.step_fc_agglayer_origin)
        self.stackedWidget.addWidget(self.step_fc_agglayer_from_canvas)
        self.stackedWidget.addWidget(self.step_fc_agglayer_from_browser)
        self.stackedWidget.addWidget(self.step_fc_agglayer_disjoint)
        self.stackedWidget.addWidget(self.step_fc_extent)
        self.stackedWidget.addWidget(self.step_fc_extent_disjoint)
        self.stackedWidget.addWidget(self.step_fc_summary)
        self.stackedWidget.addWidget(self.step_fc_analysis)

        self.stackedWidget.addWidget(self.wizard_help)

        # QSetting
        self.setting = QSettings()

        # Wizard Steps
        self.impact_function_steps = []
        self.keyword_steps = []
        self.on_help = False

        self.resized.connect(self.after_resize)

    def set_mode_label_to_keywords_creation(self):
        """Set the mode label to the Keywords Creation/Update mode."""
        self.setWindowTitle(self.keyword_creation_wizard_name)
        if self.get_existing_keyword('layer_purpose'):
            mode_name = tr(
                'Keywords update wizard for layer <b>{layer_name}</b>').format(
                    layer_name=self.layer.name())
        else:
            mode_name = tr(
                'Keywords creation wizard for layer <b>{layer_name}</b>'
            ).format(layer_name=self.layer.name())
        self.lblSubtitle.setText(mode_name)

    def set_mode_label_to_ifcw(self):
        """Set the mode label to the IFCW."""
        self.setWindowTitle(self.ifcw_name)
        self.lblSubtitle.setText(
            tr('Use this wizard to run a guided impact assessment'))

    def set_keywords_creation_mode(self, layer=None, keywords=None):
        """Set the Wizard to the Keywords Creation mode.

        :param layer: Layer to set the keywords for
        :type layer: QgsMapLayer

        :param keywords: Keywords for the layer.
        :type keywords: dict, None
        """
        self.layer = layer or self.iface.mapCanvas().currentLayer()

        if keywords is not None:
            self.existing_keywords = keywords
        else:
            # Always read from metadata file.
            try:
                self.existing_keywords = self.keyword_io.read_keywords(
                    self.layer)
            except (HashNotFoundError, OperationalError, NoKeywordsFoundError,
                    KeywordNotFoundError, InvalidParameterError,
                    UnsupportedProviderError, MetadataReadError):
                self.existing_keywords = None
        self.set_mode_label_to_keywords_creation()

        step = self.step_kw_purpose
        step.set_widgets()
        self.go_to_step(step)

    def set_function_centric_mode(self):
        """Set the Wizard to the Function Centric mode."""
        self.set_mode_label_to_ifcw()

        step = self.step_fc_functions1
        step.set_widgets()
        self.go_to_step(step)

    def field_keyword_for_the_layer(self):
        """Return the proper keyword for field for the current layer.

        :returns: the field keyword
        :rtype: str
        """
        layer_purpose_key = self.step_kw_purpose.selected_purpose()['key']
        if layer_purpose_key == layer_purpose_aggregation['key']:
            return get_compulsory_fields(layer_purpose_key)['key']
        elif layer_purpose_key in [
                layer_purpose_exposure['key'], layer_purpose_hazard['key']
        ]:
            layer_subcategory_key = \
                self.step_kw_subcategory.selected_subcategory()['key']
            return get_compulsory_fields(layer_purpose_key,
                                         layer_subcategory_key)['key']
        else:
            raise InvalidParameterError

    def get_parent_mode_constraints(self):
        """Return the category and subcategory keys to be set in the
        subordinate mode.

        :returns: (the category definition, the hazard/exposure definition)
        :rtype: (dict, dict)
        """
        h, e, _hc, _ec = self.selected_impact_function_constraints()
        if self.parent_step in [
                self.step_fc_hazlayer_from_canvas,
                self.step_fc_hazlayer_from_browser
        ]:
            category = layer_purpose_hazard
            subcategory = h
        elif self.parent_step in [
                self.step_fc_explayer_from_canvas,
                self.step_fc_explayer_from_browser
        ]:
            category = layer_purpose_exposure
            subcategory = e
        elif self.parent_step:
            category = layer_purpose_aggregation
            subcategory = None
        else:
            category = None
            subcategory = None
        return category, subcategory

    def selected_impact_function_constraints(self):
        """Obtain impact function constraints selected by user.

        :returns: Tuple of metadata of hazard, exposure,
            hazard layer geometry and exposure layer geometry
        :rtype: tuple
        """

        hazard = self.step_fc_functions1.selected_value(
            layer_purpose_hazard['key'])
        exposure = self.step_fc_functions1.selected_value(
            layer_purpose_exposure['key'])

        hazard_geometry = self.step_fc_functions2.selected_value(
            layer_purpose_hazard['key'])
        exposure_geometry = self.step_fc_functions2.selected_value(
            layer_purpose_exposure['key'])
        return hazard, exposure, hazard_geometry, exposure_geometry

    def is_layer_compatible(self, layer, layer_purpose=None, keywords=None):
        """Validate if a given layer is compatible for selected IF
           as a given layer_purpose

        :param layer: The layer to be validated
        :type layer: QgsVectorLayer | QgsRasterLayer

        :param layer_purpose: The layer_purpose the layer is validated for
        :type layer_purpose: None, string

        :param keywords: The layer keywords
        :type keywords: None, dict

        :returns: True if layer is appropriate for the selected role
        :rtype: boolean
        """

        # If not explicitly stated, find the desired purpose
        # from the parent step
        if not layer_purpose:
            layer_purpose = self.get_parent_mode_constraints()[0]['key']

        # If not explicitly stated, read the layer's keywords
        if not keywords:
            try:
                keywords = self.keyword_io.read_keywords(layer)
                if 'layer_purpose' not in keywords:
                    keywords = None
            except (HashNotFoundError, OperationalError, NoKeywordsFoundError,
                    KeywordNotFoundError, InvalidParameterError,
                    UnsupportedProviderError):
                keywords = None

        # Get allowed subcategory and layer_geometry from IF constraints
        h, e, hc, ec = self.selected_impact_function_constraints()
        if layer_purpose == 'hazard':
            subcategory = h['key']
            layer_geometry = hc['key']
        elif layer_purpose == 'exposure':
            subcategory = e['key']
            layer_geometry = ec['key']
        else:
            # For aggregation layers, use a simplified test and return
            if (keywords and 'layer_purpose' in keywords
                    and keywords['layer_purpose'] == layer_purpose):
                return True
            if not keywords and is_polygon_layer(layer):
                return True
            return False

        # Compare layer properties with explicitly set constraints
        # Reject if layer geometry doesn't match
        if layer_geometry != self.get_layer_geometry_key(layer):
            return False

        # If no keywords, there's nothing more we can check.
        # The same if the keywords version doesn't match
        if not keywords or 'keyword_version' not in keywords:
            return True
        keyword_version = str(keywords['keyword_version'])
        if not is_keyword_version_supported(keyword_version):
            return True

        # Compare layer keywords with explicitly set constraints
        # Reject if layer purpose missing or doesn't match
        if ('layer_purpose' not in keywords
                or keywords['layer_purpose'] != layer_purpose):
            return False

        # Reject if layer subcategory doesn't match
        if (layer_purpose in keywords
                and keywords[layer_purpose] != subcategory):
            return False

        return True

    def get_compatible_canvas_layers(self, category):
        """Collect layers from map canvas, compatible for the given category
           and selected impact function

        .. note:: Returns layers with keywords and layermode matching
           the category and compatible with the selected impact function.
           Also returns layers without keywords with layermode
           compatible with the selected impact function.

        :param category: The category to filter for.
        :type category: string

        :returns: Metadata of found layers.
        :rtype: list of dicts
        """

        # Collect compatible layers
        layers = []
        for layer in self.iface.mapCanvas().layers():
            try:
                keywords = self.keyword_io.read_keywords(layer)
                if 'layer_purpose' not in keywords:
                    keywords = None
            except (HashNotFoundError, OperationalError, NoKeywordsFoundError,
                    KeywordNotFoundError, InvalidParameterError,
                    UnsupportedProviderError):
                keywords = None

            if self.is_layer_compatible(layer, category, keywords):
                layers += [{
                    'id': layer.id(),
                    'name': layer.name(),
                    'keywords': keywords
                }]

        # Move layers without keywords to the end
        l1 = [l for l in layers if l['keywords']]
        l2 = [l for l in layers if not l['keywords']]
        layers = l1 + l2

        return layers

    def get_layer_geometry_key(self, layer=None):
        """Obtain layer mode of a given layer.

        If no layer specified, the current layer is used

        :param layer : layer to examine
        :type layer: QgsMapLayer or None

        :returns: The layer mode.
        :rtype: str
        """
        if not layer:
            layer = self.layer
        if is_raster_layer(layer):
            return layer_geometry_raster['key']
        elif is_point_layer(layer):
            return layer_geometry_point['key']
        elif is_polygon_layer(layer):
            return layer_geometry_polygon['key']
        else:
            return layer_geometry_line['key']

    def get_existing_keyword(self, keyword):
        """Obtain an existing keyword's value.

        :param keyword: A keyword from keywords.
        :type keyword: str

        :returns: The value of the keyword.
        :rtype: str, QUrl
        """
        if self.existing_keywords is None:
            return {}
        if keyword is not None:
            return self.existing_keywords.get(keyword, {})
        else:
            return {}

    def get_layer_description_from_canvas(self, layer, purpose):
        """Obtain the description of a canvas layer selected by user.

        :param layer: The QGIS layer.
        :type layer: QgsMapLayer

        :param purpose: The layer purpose of the layer to get the description.
        :type purpose: string

        :returns: description of the selected layer.
        :rtype: string
        """
        if not layer:
            return ""

        try:
            keywords = self.keyword_io.read_keywords(layer)
            if 'layer_purpose' not in keywords:
                keywords = None
        except (HashNotFoundError, OperationalError, NoKeywordsFoundError,
                KeywordNotFoundError, InvalidParameterError,
                UnsupportedProviderError):
            keywords = None

        # set the current layer (e.g. for the keyword creation sub-thread)
        self.layer = layer
        if purpose == layer_purpose_hazard['key']:
            self.hazard_layer = layer
        elif purpose == layer_purpose_exposure['key']:
            self.exposure_layer = layer
        else:
            self.aggregation_layer = layer

        # Check if the layer is keywordless
        if keywords and 'keyword_version' in keywords:
            kw_ver = str(keywords['keyword_version'])
            self.is_selected_layer_keywordless = (
                not is_keyword_version_supported(kw_ver))
        else:
            self.is_selected_layer_keywordless = True

        description = layer_description_html(layer, keywords)
        return description

    # ===========================
    # NAVIGATION
    # ===========================

    def go_to_step(self, step):
        """Set the stacked widget to the given step, set up the buttons,
           and run all operations that should start immediately after
           entering the new step.

        :param step: The step widget to be moved to.
        :type step: WizardStep
        """
        self.stackedWidget.setCurrentWidget(step)

        # Disable the Next button unless new data already entered
        self.pbnNext.setEnabled(step.is_ready_to_next_step())

        # Enable the Back button unless it's not the first step
        self.pbnBack.setEnabled(
            step not in [self.step_kw_purpose, self.step_fc_functions1]
            or self.parent_step is not None)

        # Set Next button label
        if (step in [self.step_kw_summary, self.step_fc_analysis]
                and self.parent_step is None):
            self.pbnNext.setText(tr('Finish'))
        elif step == self.step_fc_summary:
            self.pbnNext.setText(tr('Run'))
        else:
            self.pbnNext.setText(tr('Next'))

        # Run analysis after switching to the new step
        if step == self.step_fc_analysis:
            self.step_fc_analysis.setup_and_run_analysis()

        # Set lblSelectCategory label if entering the kw mode
        # from the ifcw mode
        if step == self.step_kw_purpose and self.parent_step:
            if self.parent_step in [
                    self.step_fc_hazlayer_from_canvas,
                    self.step_fc_hazlayer_from_browser
            ]:
                text_label = category_question_hazard
            elif self.parent_step in [
                    self.step_fc_explayer_from_canvas,
                    self.step_fc_explayer_from_browser
            ]:
                text_label = category_question_exposure
            else:
                text_label = category_question_aggregation
            self.step_kw_purpose.lblSelectCategory.setText(text_label)

    # prevents actions being handled twice
    # noinspection PyPep8Naming
    @pyqtSignature('')
    def on_pbnNext_released(self):
        """Handle the Next button release.

        .. note:: This is an automatic Qt slot
           executed when the Next button is released.
        """
        current_step = self.get_current_step()

        if current_step == self.step_kw_fields_mapping:
            try:
                self.step_kw_fields_mapping.get_field_mapping()
            except InvalidValidationException as e:
                display_warning_message_box(self, tr('Invalid Field Mapping'),
                                            get_string(e.message))
                return

        if current_step.step_type == STEP_FC:
            self.impact_function_steps.append(current_step)
        elif current_step.step_type == STEP_KW:
            self.keyword_steps.append(current_step)
        else:
            LOGGER.debug(current_step.step_type)
            raise InvalidWizardStep

        # Save keywords if it's the end of the keyword creation mode
        if current_step == self.step_kw_summary:
            self.save_current_keywords()

        # After any step involving Browser, add selected layer to map canvas
        if current_step in [
                self.step_fc_hazlayer_from_browser,
                self.step_fc_explayer_from_browser,
                self.step_fc_agglayer_from_browser
        ]:
            if not QgsMapLayerRegistry.instance().mapLayersByName(
                    self.layer.name()):
                QgsMapLayerRegistry.instance().addMapLayers([self.layer])

                # Make the layer visible. Might be hidden by default. See #2925
                legend = self.iface.legendInterface()
                legend.setLayerVisible(self.layer, True)

        # After the extent selection, save the extent and disconnect signals
        if current_step == self.step_fc_extent:
            self.step_fc_extent.write_extent()

        # Determine the new step to be switched
        new_step = current_step.get_next_step()

        if new_step is not None:
            # Prepare the next tab
            new_step.set_widgets()
        else:
            # Wizard complete
            self.accept()
            return

        self.go_to_step(new_step)

    # prevents actions being handled twice
    # noinspection PyPep8Naming
    @pyqtSignature('')
    def on_pbnBack_released(self):
        """Handle the Back button release.

        .. note:: This is an automatic Qt slot
           executed when the Back button is released.
        """
        current_step = self.get_current_step()
        if current_step.step_type == STEP_FC:
            new_step = self.impact_function_steps.pop()
        elif current_step.step_type == STEP_KW:
            try:
                new_step = self.keyword_steps.pop()
            except IndexError:
                new_step = self.impact_function_steps.pop()
        else:
            raise InvalidWizardStep

        # set focus to table widgets, as the inactive selection style is gray
        if new_step == self.step_fc_functions1:
            self.step_fc_functions1.tblFunctions1.setFocus()
        if new_step == self.step_fc_functions2:
            self.step_fc_functions2.tblFunctions2.setFocus()
        # Re-connect disconnected signals when coming back to the Extent step
        if new_step == self.step_fc_extent:
            self.step_fc_extent.set_widgets()
        # Set Next button label
        self.pbnNext.setText(tr('Next'))
        self.pbnNext.setEnabled(True)
        self.go_to_step(new_step)

    # prevents actions being handled twice
    # noinspection PyPep8Naming
    @pyqtSignature('')
    def on_pbnHelp_released(self):
        if self.on_help:
            self.pbnHelp.setText(tr('Show help'))
            self.wizard_help.restore_button_state()
            self.stackedWidget.setCurrentWidget(self.wizard_help.wizard_step)
        else:
            self.pbnHelp.setText(tr('Hide help'))
            self.wizard_help.show_help(self.get_current_step())
            self.stackedWidget.setCurrentWidget(self.wizard_help)

        self.on_help = not self.on_help

    def get_current_step(self):
        """Return current step of the wizard.

        :returns: Current step of the wizard.
        :rtype: WizardStep instance
        """
        return self.stackedWidget.currentWidget()

    def get_keywords(self):
        """Obtain the state of the dialog as a keywords dict.

        :returns: Keywords reflecting the state of the dialog.
        :rtype: dict
        """
        keywords = {}
        inasafe_fields = {}
        keywords['layer_geometry'] = self.get_layer_geometry_key()
        if self.step_kw_purpose.selected_purpose():
            keywords['layer_purpose'] = self.step_kw_purpose.\
                selected_purpose()['key']
        if self.step_kw_subcategory.selected_subcategory():
            key = self.step_kw_purpose.selected_purpose()['key']
            keywords[key] = self.step_kw_subcategory.\
                selected_subcategory()['key']
        if self.get_layer_geometry_key() == layer_geometry_raster['key']:
            if self.step_kw_band_selector.selected_band():
                keywords['active_band'] = self.step_kw_band_selector.\
                    selected_band()
        if keywords['layer_purpose'] == layer_purpose_hazard['key']:
            if self.step_kw_hazard_category.selected_hazard_category():
                keywords['hazard_category'] \
                    = self.step_kw_hazard_category.\
                    selected_hazard_category()['key']
        if self.step_kw_layermode.selected_layermode():
            keywords['layer_mode'] = self.step_kw_layermode.\
                selected_layermode()['key']
        if self.step_kw_unit.selected_unit():
            if self.step_kw_purpose.selected_purpose() == layer_purpose_hazard:
                key = continuous_hazard_unit['key']
            else:
                key = exposure_unit['key']
            keywords[key] = self.step_kw_unit.selected_unit()['key']
        if self.step_kw_field.selected_fields():
            field_key = self.field_keyword_for_the_layer()
            inasafe_fields[field_key] = self.step_kw_field.selected_fields()
        if self.step_kw_classification.selected_classification():
            keywords['classification'] = self.step_kw_classification.\
                selected_classification()['key']

        if keywords['layer_purpose'] == layer_purpose_hazard['key']:
            multi_classifications = self.step_kw_multi_classifications.\
                get_current_state()
            value_maps = multi_classifications.get('value_maps')
            if value_maps is not None:
                keywords['value_maps'] = value_maps
            thresholds = multi_classifications.get('thresholds')
            if thresholds is not None:
                keywords['thresholds'] = thresholds
        else:
            if self.step_kw_layermode.selected_layermode():
                layer_mode = self.step_kw_layermode.selected_layermode()
                if layer_mode == layer_mode_continuous:
                    thresholds = self.step_kw_threshold.get_threshold()
                    if thresholds:
                        keywords['thresholds'] = thresholds
                elif layer_mode == layer_mode_classified:
                    value_map = self.step_kw_classify.selected_mapping()
                    if value_map:
                        keywords['value_map'] = value_map

        if self.step_kw_source.leSource.text():
            keywords['source'] = get_unicode(
                self.step_kw_source.leSource.text())
        if self.step_kw_source.leSource_url.text():
            keywords['url'] = get_unicode(
                self.step_kw_source.leSource_url.text())
        if self.step_kw_source.leSource_scale.text():
            keywords['scale'] = get_unicode(
                self.step_kw_source.leSource_scale.text())
        if self.step_kw_source.ckbSource_date.isChecked():
            keywords['date'] = self.step_kw_source.dtSource_date.dateTime()
        if self.step_kw_source.leSource_license.text():
            keywords['license'] = get_unicode(
                self.step_kw_source.leSource_license.text())
        if self.step_kw_title.leTitle.text():
            keywords['title'] = get_unicode(self.step_kw_title.leTitle.text())

        inasafe_fields.update(self.step_kw_inasafe_fields.get_inasafe_fields())
        inasafe_fields.update(
            self.step_kw_default_inasafe_fields.get_inasafe_fields())
        inasafe_fields.update(
            self.step_kw_fields_mapping.get_field_mapping()['fields'])

        if inasafe_fields:
            keywords['inasafe_fields'] = inasafe_fields

        inasafe_default_values = {}
        if keywords['layer_geometry'] == layer_geometry_raster['key']:
            pass
            # Notes(IS): Skipped assigning raster inasafe default value for
            # now.
            # inasafe_default_values = self.\
            #     step_kw_inasafe_raster_default_values.\
            #     get_inasafe_default_values()
        else:
            inasafe_default_values.update(self.step_kw_default_inasafe_fields.
                                          get_inasafe_default_values())
            inasafe_default_values.update(
                self.step_kw_fields_mapping.get_field_mapping()['values'])

        if inasafe_default_values:
            keywords['inasafe_default_values'] = inasafe_default_values

        return keywords

    def save_current_keywords(self):
        """Save keywords to the layer.

        It will write out the keywords for the current layer.
        This method is based on the KeywordsDialog class.
        """
        current_keywords = self.get_keywords()
        try:
            self.keyword_io.write_keywords(layer=self.layer,
                                           keywords=current_keywords)
        except InaSAFEError, e:
            error_message = get_error_message(e)
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QtGui.QMessageBox.warning(
                self, tr('InaSAFE'),
                tr('An error was encountered when saving the following '
                   'keywords:\n {error_message}').format(
                       error_message=error_message.to_html()))
        if self.dock is not None:
            # noinspection PyUnresolvedReferences
            self.dock.get_layers()

        # Save default value to QSetting
        if current_keywords.get('inasafe_default_values'):
            for key, value in (
                    current_keywords['inasafe_default_values'].items()):
                set_inasafe_default_value_qsetting(self.setting, RECENT, key,
                                                   value)
Exemplo n.º 9
0
class FieldMappingDialog(QDialog, FORM_CLASS):

    """Dialog implementation class for the InaSAFE field mapping tool."""

    def __init__(self, parent=None, iface=None, setting=None):
        """Constructor."""
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.setWindowTitle(self.tr('InaSAFE Field Mapping Tool'))
        icon = resources_path('img', 'icons', 'show-mapping-tool.svg')
        self.setWindowIcon(QIcon(icon))
        self.parent = parent
        self.iface = iface
        if setting is None:
            setting = QSettings()
        self.setting = setting

        self.keyword_io = KeywordIO()

        self.layer = None
        self.metadata = {}

        self.layer_input_layout = QHBoxLayout()
        self.layer_label = QLabel(tr('Layer'))
        self.layer_combo_box = QgsMapLayerComboBox()
        # Filter only for Polygon and Point
        self.layer_combo_box.setFilters(
            QgsMapLayerProxyModel.PolygonLayer |
            QgsMapLayerProxyModel.PointLayer)
        # Filter out a layer that don't have layer groups
        excepted_layers = []
        for i in range(self.layer_combo_box.count()):
            layer = self.layer_combo_box.layer(i)
            try:
                keywords = self.keyword_io.read_keywords(layer)
            except (KeywordNotFoundError, NoKeywordsFoundError):
                excepted_layers.append(layer)
                continue
            layer_purpose = keywords.get('layer_purpose')
            if not layer_purpose:
                excepted_layers.append(layer)
                continue
            if layer_purpose == layer_purpose_exposure['key']:
                layer_subcategory = keywords.get('exposure')
            elif layer_purpose == layer_purpose_hazard['key']:
                layer_subcategory = keywords.get('hazard')
            else:
                layer_subcategory = None

            field_groups = get_field_groups(layer_purpose, layer_subcategory)
            if len(field_groups) == 0:
                excepted_layers.append(layer)
                continue
        self.layer_combo_box.setExceptedLayerList(excepted_layers)

        # Select the active layer.
        if self.iface.activeLayer():
            found = self.layer_combo_box.findText(
                self.iface.activeLayer().name())
            if found > -1:
                self.layer_combo_box.setLayer(self.iface.activeLayer())

        self.field_mapping_widget = None
        self.main_stacked_widget.setCurrentIndex(1)

        # Input
        self.layer_input_layout.addWidget(self.layer_label)
        self.layer_input_layout.addWidget(self.layer_combo_box)

        self.header_label = QLabel()
        self.header_label.setWordWrap(True)
        self.main_layout.addWidget(self.header_label)
        self.main_layout.addLayout(self.layer_input_layout)

        # Signal
        self.layer_combo_box.layerChanged.connect(self.set_layer)

        if self.layer_combo_box.currentLayer():
            self.set_layer(self.layer_combo_box.currentLayer())

        # Set up things for context help
        self.help_button = self.button_box.button(QDialogButtonBox.Help)
        # Allow toggling the help button
        self.help_button.setCheckable(True)
        self.help_button.toggled.connect(self.help_toggled)

        # Set up things for ok button
        self.ok_button = self.button_box.button(QDialogButtonBox.Ok)
        self.ok_button.clicked.connect(self.accept)

        # Set up things for cancel button
        self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel)
        self.cancel_button.clicked.connect(self.reject)

    def set_layer(self, layer=None, keywords=None):
        """Set layer and update UI accordingly.

        :param layer: A QgsVectorLayer.
        :type layer: QgsVectorLayer

        :param keywords: Keywords for the layer.
        :type keywords: dict, None
        """
        if self.field_mapping_widget is not None:
            self.field_mapping_widget.setParent(None)
            self.field_mapping_widget.close()
            self.field_mapping_widget.deleteLater()
            self.main_layout.removeWidget(self.field_mapping_widget)

        if layer:
            self.layer = layer
        else:
            self.layer = self.layer_combo_box.currentLayer()
        if not self.layer:
            return

        if keywords is not None:
            self.metadata = keywords
        else:
            # Always read from metadata file.
            try:
                self.metadata = self.keyword_io.read_keywords(self.layer)
            except (
                    NoKeywordsFoundError,
                    KeywordNotFoundError,
                    MetadataReadError) as e:
                raise e
        if 'inasafe_default_values' not in self.metadata:
            self.metadata['inasafe_default_values'] = {}
        if 'inasafe_fields' not in self.metadata:
            self.metadata['inasafe_fields'] = {}
        self.field_mapping_widget = FieldMappingWidget(
            parent=self, iface=self.iface)
        self.field_mapping_widget.set_layer(self.layer, self.metadata)
        self.field_mapping_widget.show()
        self.main_layout.addWidget(self.field_mapping_widget)

        # Set header label
        group_names = [
            self.field_mapping_widget.tabText(i) for i in range(
                self.field_mapping_widget.count())]
        if len(group_names) == 0:
            header_text = tr(
                'There is no field group for this layer. Please select '
                'another layer.')
            self.header_label.setText(header_text)
            return
        elif len(group_names) == 1:
            pretty_group_name = group_names[0]
        elif len(group_names) == 2:
            pretty_group_name = group_names[0] + tr(' and ') + group_names[1]
        else:
            pretty_group_name = ', '.join(group_names[:-1])
            pretty_group_name += tr(', and {0}').format(group_names[-1])
        header_text = tr(
            'Please fill the information for every tab to determine the '
            'attribute for {0} group.').format(pretty_group_name)
        self.header_label.setText(header_text)

    @pyqtSlot()
    @pyqtSignature('bool')  # prevents actions being handled twice
    def help_toggled(self, flag):
        """Show or hide the help tab in the stacked widget.

        .. versionadded: 3.2.1

        :param flag: Flag indicating whether help should be shown or hidden.
        :type flag: bool
        """
        if flag:
            self.help_button.setText(self.tr('Hide Help'))
            self.show_help()
        else:
            self.help_button.setText(self.tr('Show Help'))
            self.hide_help()

    def hide_help(self):
        """Hide the usage info from the user.

        .. versionadded: 3.2.1
        """
        self.main_stacked_widget.setCurrentIndex(1)

    def show_help(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        self.main_stacked_widget.setCurrentIndex(0)
        header = html_header()
        footer = html_footer()

        string = header

        message = field_mapping_help()

        string += message.to_html()
        string += footer

        self.help_web_view.setHtml(string)

    def save_metadata(self):
        """Save metadata based on the field mapping state."""
        metadata = self.field_mapping_widget.get_field_mapping()
        for key, value in metadata['fields'].items():
            # Delete the key if it's set to None
            if key in self.metadata['inasafe_default_values']:
                self.metadata['inasafe_default_values'].pop(key)
            if value is None or value == []:
                if key in self.metadata['inasafe_fields']:
                    self.metadata['inasafe_fields'].pop(key)
            else:
                self.metadata['inasafe_fields'][key] = value

        for key, value in metadata['values'].items():
            # Delete the key if it's set to None
            if key in self.metadata['inasafe_fields']:
                self.metadata['inasafe_fields'].pop(key)
            if value is None:
                if key in self.metadata['inasafe_default_values']:
                    self.metadata['inasafe_default_values'].pop(key)
            else:
                self.metadata['inasafe_default_values'][key] = value

        # Save metadata
        try:
            self.keyword_io.write_keywords(
                layer=self.layer, keywords=self.metadata)
        except InaSAFEError, e:
            error_message = get_error_message(e)
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QMessageBox.warning(
                self, self.tr('InaSAFE'),
                ((self.tr(
                    'An error was encountered when saving the following '
                    'keywords:\n %s') % error_message.to_html())))

        # Update setting fir recent value
        if self.metadata.get('inasafe_default_values'):
            for key, value in \
                    self.metadata['inasafe_default_values'].items():
                set_inasafe_default_value_qsetting(
                    self.setting, key, RECENT, value)