Beispiel #1
0
    def validate_input(self):
        """Validate the input before saving a scenario.

        Those validations are:
        1. self.exposure_layer must be not None
        2. self.hazard_layer must be not None
        3. self.function_id is not an empty string or None
        """
        self.exposure_layer = self.dock.get_exposure_layer()
        self.hazard_layer = self.dock.get_hazard_layer()
        self.aggregation_layer = self.dock.get_aggregation_layer()

        is_valid = True
        warning_message = None
        if self.exposure_layer is None:
            warning_message = tr(
                'Exposure layer is not found, can not save scenario. Please '
                'add exposure layer to do so.')
            is_valid = False

        if self.hazard_layer is None:
            warning_message = tr(
                'Hazard layer is not found, can not save scenario. Please add '
                'hazard layer to do so.')
            is_valid = False

        return is_valid, warning_message
Beispiel #2
0
def show_keywords_need_review_message(sender, message=None):
    """Show a message keywords are not adequate to run an analysis.

    .. versionadded: 4.0

    :param message: Additional message to display.
    :type message: str

    .. note:: The print button will be disabled if this method is called.
    """
    LOGGER.debug('Showing incorrect keywords for v4 message')
    message = generate_input_error_message(
        tr('Layer Keywords Outdated:'),
        m.Paragraph(
            tr(
                'Please update the keywords for your layers and then '
                'try to run the analysis again. Use the keyword wizard '),
            m.Image(
                'file:///%s/img/icons/'
                'show-keyword-wizard.svg' % resources_path(),
                **SMALL_ICON_STYLE),
            tr(
                ' icon in the toolbar to update your layer\'s keywords.'),
            message)
    )
    send_static_message(sender, message)
def threshold():
    """Generator for the default threshold parameter.

    :return: List of InputListParameter
    :rtype: list[InputListParameter]
    """
    field = InputListParameter()
    field.name = 'Thresholds [m]'
    field.is_required = True
    field.element_type = float
    field.expected_type = list
    field.ordering = InputListParameter.AscendingOrder
    field.minimum_item_count = 1
    # Rizky: no reason for the number below. It can be any values to describe
    # maximum item count. Feel free to change it when necessary.
    # PS: it was my birthdate
    field.maximum_item_count = 19
    field.value = [1.0]  # default value
    field.help_text = tr(
        'Thresholds value to categorize inundated area.')
    field.description = tr(
        'Up to three thresholds (in meters) can be set in an increasing '
        'order. The impact function will report the number of people per '
        'threshold you define here. Specify the upper bound for each '
        'threshold. The lower bound of the first threshold shall be zero. '
        'People in water depths above the maximum threshold will be '
        'classified as needing evacuation.')
    return field
        def get_metadata():
            """Return metadata as a dictionary.

            This is a static method. You can use it to get the metadata in
            dictionary format for an impact function.

            :returns: A dictionary representing all the metadata for the
                concrete impact function.
            :rtype: dict
            """
            dict_meta = {
                'id': 'ContinuousHazardPopulationImpactFunction',
                'name': tr('Continuous Hazard Population Impact Function'),
                'impact': tr('Be impacted'),
                'author': 'AIFDR',
                'date_implemented': 'N/A',
                'overview': tr(
                    'To assess the impacts of continuous hazards in raster '
                    'format on population raster layer.'),
                'categories': {
                    'hazard': {
                        'definition': hazard_definition,
                        'subcategories': hazard_all,  # already a list
                        'units': [],
                        'layer_constraints': [layer_raster_continuous]
                    },
                    'exposure': {
                        'definition': exposure_definition,
                        'subcategories': [exposure_population],
                        'units': [unit_people_per_pixel],
                        'layer_constraints': [layer_raster_continuous]
                    }
                }
            }
            return dict_meta
Beispiel #5
0
def show_keyword_version_message(sender, keyword_version, inasafe_version):
    """Show a message indicating that the keywords version is mismatch

    .. versionadded: 3.2

    :param keyword_version: The version of the layer's keywords
    :type keyword_version: str

    :param inasafe_version: The version of the InaSAFE
    :type inasafe_version: str

    .. note:: The print button will be disabled if this method is called.
    """
    LOGGER.debug('Showing Mismatch Version Message')
    message = generate_input_error_message(
        tr('Layer Keyword\'s Version Mismatch:'),
        m.Paragraph(
            tr(
                'Your layer\'s keyword\'s version ({layer_version}) does not '
                'match with your InaSAFE version ({inasafe_version}). If you '
                'wish to use it as an exposure, hazard, or aggregation layer '
                'in an analysis, please use the keyword wizard to update the '
                'keywords. You can open the wizard by clicking on '
                'the ').format(
                layer_version=keyword_version,
                inasafe_version=inasafe_version),
            m.Image(
                'file:///%s/img/icons/'
                'show-keyword-wizard.svg' % resources_path(),
                **SMALL_ICON_STYLE),
            tr(
                ' icon in the toolbar.'))
    )
    send_static_message(sender, message)
Beispiel #6
0
    def _affected_categories(self):
        """Overwriting the affected categories, since 'unaffected' are counted.

        :returns: The categories that equal affected.
        :rtype: list
        """
        return [tr('Flooded'), tr('Wet')]
Beispiel #7
0
    def notes(self):
        """Return the notes section of the report.

        :return: The notes that should be attached to this impact report.
        :rtype: list
        """
        volcano_names = self.volcano_names
        return [
            {
                'content': tr('Notes'),
                'header': True
            },
            {
                'content': tr(
                    'Map shows buildings affected in each of the '
                    'volcano buffered zones.')
            },
            {
                'content': tr(
                    'Only buildings available in OpenStreetMap '
                    'are considered.')
            },
            {
                'content': tr('Volcanoes considered: %s.') % volcano_names,
                'header': True
            }
        ]
    def as_dict():
        """Return metadata as a dictionary.

        This is a static method. You can use it to get the metadata in
        dictionary format for an impact function.

        :returns: A dictionary representing all the metadata for the
            concrete impact function.
        :rtype: dict
        """
        dict_meta = {
            'id': 'FloodRasterRoadsFunction',
            'name': tr('Raster flood on roads'),
            'impact': tr('Be flooded in given thresholds'),
            'title': tr('Be flooded in given thresholds'),
            'function_type': 'qgis2.0',
            'author': 'Dmitry Kolesov',
            'date_implemented': 'N/A',
            'overview': tr('N/A'),
            'detailed_description': '',
            'hazard_input': '',
            'exposure_input': '',
            'output': '',
            'actions': '',
            'limitations': [],
            'citations': [],
            'layer_requirements': {
                'hazard': {
                    'layer_mode': layer_mode_continuous,
                    'layer_geometries': [layer_geometry_raster],
                    'hazard_categories': [
                        hazard_category_single_event,
                        hazard_category_multiple_event
                    ],
                    'hazard_types': [hazard_flood],
                    'continuous_hazard_units': [unit_feet, unit_metres],
                    'vector_hazard_classifications': [],
                    'raster_hazard_classifications': [],
                    'additional_keywords': []
                },
                'exposure': {
                    'layer_mode': layer_mode_classified,
                    'layer_geometries': [layer_geometry_line],
                    'exposure_types': [exposure_road],
                    'exposure_units': [],
                    'exposure_class_fields': [road_class_field],
                    'additional_keywords': []
                }
            },
            'parameters': OrderedDict([
                ('min threshold',
                 parameter_definitions.min_threshold()),
                ('max threshold',
                 parameter_definitions.max_threshold()),
                ('postprocessors', OrderedDict([
                    ('RoadType', road_type_postprocessor())
                ]))
            ])
        }
        return dict_meta
Beispiel #9
0
    def notes(self):
        """Return the notes section of the report.

        :return: The notes that should be attached to this impact report.
        :rtype: list
        """
        if get_needs_provenance_value(self.parameters) is None:
            needs_provenance = ''
        else:
            needs_provenance = tr(get_needs_provenance_value(self.parameters))
        fields = [
            tr('Map shows buildings affected in each of the volcano buffered '
               'zones.'),
            tr('Total population in the analysis area: %s') %
            population_rounding(self.total_population),
            tr('<sup>1</sup>People need evacuation if they are within the '
               'volcanic hazard zones.'),
            tr('Volcanoes considered: %s.') % self.volcano_names,
            needs_provenance
        ]

        if self.no_data_warning:
            fields = fields + no_data_warning
        # include any generic exposure specific notes from definitions.py
        fields = fields + self.exposure_notes()
        # include any generic hazard specific notes from definitions.py
        fields = fields + self.hazard_notes()
        return fields
Beispiel #10
0
    def notes(self):
        """Return the notes section of the report.

        :return: The notes that should be attached to this impact report.
        :rtype: list
        """
        if get_needs_provenance_value(self.parameters) is None:
            needs_provenance = ''
        else:
            needs_provenance = tr(get_needs_provenance_value(self.parameters))

        if self.volcano_names:
            sorted_volcano_names = ', '.join(sorted(self.volcano_names))
        else:
            sorted_volcano_names = tr('Not specified in data')

        fields = [
            tr('Total population in the analysis area: %s') %
            format_int(population_rounding(self.total_population)),
            tr('<sup>1</sup>People need evacuation if they are within the '
               'volcanic hazard zones.'),
            tr('Volcanoes considered: %s.') % sorted_volcano_names
        ]

        if needs_provenance:
            fields.append(needs_provenance)

        if self.no_data_warning:
            fields = fields + no_data_warning

        # include any generic exposure specific notes from definitions.py
        fields = fields + self.exposure_notes()
        # include any generic hazard specific notes from definitions.py
        fields = fields + self.hazard_notes()
        return fields
Beispiel #11
0
    def notes(self):
        """Return the notes section of the report.

        :return: The notes that should be attached to this impact report.
        :rtype: safe.messaging.Message
        """
        message = m.Message(style_class='container')
        message.add(
            m.Heading(tr('Notes and assumptions'), **styles.INFO_STYLE))
        checklist = m.BulletedList()

        # Thresholds for mmi breakdown.
        t0 = self.parameters['low_threshold'].value
        t1 = self.parameters['medium_threshold'].value
        t2 = self.parameters['high_threshold'].value
        is_nexis = self.is_nexis

        checklist.add(tr(
            'High hazard is defined as shake levels greater '
            'than %i on the MMI scale.') % t2)

        checklist.add(tr(
            'Medium hazard is defined as shake levels '
            'between %i and %i on the MMI scale.') % (t1, t2))

        checklist.add(tr(
            'Low hazard is defined as shake levels '
            'between %i and %i on the MMI scale.') % (t0, t1))

        if is_nexis:
            checklist.add(tr(
                'Values are in units of 1 million Australian Dollars'))

        message.add(checklist)
        return message
    def lookup_category(self, category):
        """Lookup a category by its name.

        :param category: The category to be looked up.
        :type category: basestring

        :returns: The category's count.
        :rtype: int

        .. note:: The category may be any valid category, but it also includes
            'Population Not Affected', 'Unaffected Population' for unaffected
            as well as 'Total Impacted', 'People impacted',
            'Total Population Affected' for total affected population. This
            diversity is to accodate existing usages, which have evolved
            separately. We may want to update these when we have decided on a
            single convention.
        """
        if category in self.affected_population.keys():
            return self.affected_population[category]
        if category in self.other_population_counts.keys():
            return self.other_population_counts[category]
        if category in [
                tr('Population Not Affected'),
                tr('Unaffected Population')]:
            return self.unaffected_population
        if category in [
                tr('Total Impacted'),
                tr('People impacted'),
                tr('Total Population Affected')]:
            return self.total_affected_population
 def add_new_resource(self):
     """Handle add new resource requests.
     """
     parameters_widget = [
         self.parameters_scrollarea.layout().itemAt(i) for i in
         range(self.parameters_scrollarea.layout().count())][0].widget()
     parameter_widgets = [
         parameters_widget.vertical_layout.itemAt(i).widget() for i in
         range(parameters_widget.vertical_layout.count())]
     parameter_widgets[0].set_text('')
     parameter_widgets[1].set_text('')
     parameter_widgets[2].set_text('')
     parameter_widgets[3].set_text('')
     parameter_widgets[4].set_text('')
     parameter_widgets[5].set_value(10)
     parameter_widgets[6].set_value(0)
     parameter_widgets[7].set_value(100)
     parameter_widgets[8].set_text(tr('weekly'))
     parameter_widgets[9].set_text(tr(
         "A displaced person should be provided with "
         "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of "
         "{{ Resource name }}. Though no less than {{ Minimum allowed }} "
         "and no more than {{ Maximum allowed }}. This should be provided "
         "{{ Frequency }}."))
     self.stacked_widget.setCurrentWidget(self.resource_edit_page)
     # hide the close button
     self.button_box.button(QDialogButtonBox.Close).setHidden(True)
def metadata_converter_help_content():
    """Helper method that returns just the content in extent mode.

    This method was added so that the text could be reused in the
    wizard.

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    paragraph = m.Paragraph(tr(
        'This tool will convert InaSAFE 4.x keyword metadata into the '
        'metadata format used by InaSAFE 3.5. The primary reason for doing '
        'this is to prepare data for use in GeoSAFE - the online version of '
        'InaSAFE.'
    ))
    message.add(paragraph)
    paragraph = m.Paragraph(tr(
        'You should note that this tool will not touch the original data or '
        'metadata associated with a layer. Instead it will make a copy of the '
        'original layer to the place that you nominate, and create a new '
        'keywords XML file to accompany that data. This new keywords file '
        'will contain InaSAFE keywords in the 3.5 format.'
    ))
    message.add(paragraph)
    return message
    def minimum_needs_breakdown(self):
        """Breakdown by population.

        :returns: The population breakdown report.
        :rtype: list
        """
        message = m.Message(style_class='container')
        message.add(m.Heading(
            tr('Evacuated population minimum needs'),
            **styles.INFO_STYLE))
        table = m.Table(
            style_class='table table-condensed table-striped')
        table.caption = None
        total_needs = self.total_needs
        for frequency, needs in total_needs.items():
            row = m.Row()
            row.add(m.Cell(
                tr('Relief items to be provided %s' % frequency),
                header=True
            ))
            row.add(m.Cell(tr('Total'), header=True, align='right'))
            table.add(row)
            for resource in needs:
                row = m.Row()
                row.add(m.Cell(tr(resource['table name'])))
                row.add(m.Cell(
                    tr(format_int(resource['amount'])),
                    align='right'
                ))
                table.add(row)
        message.add(table)
        return message
    def format_impact_summary(self):
        """The impact summary as per category

        :returns: The impact summary.
        :rtype: safe.message.Message
        """
        attributes = self.impact_summary['attributes']
        fields = self.impact_summary['fields']

        message = m.Message(style_class='container')
        table = m.Table(style_class='table table-condensed table-striped')
        table.caption = None

        row = m.Row()
        row.add(m.Cell(tr('Summary by road type'), header=True))
        for _ in attributes:
            row.add(m.Cell('', header=True))

        row = m.Row()
        row.add(m.Cell(tr('Road Type'), header=True))
        for affected_category in attributes:
            row.add(m.Cell(tr(affected_category), header=True, align='right'))
        table.add(row)

        row = m.Row()
        row.add(m.Cell(tr('All (m)')))
        for total_affected_value in fields[0]:
            row.add(m.Cell(
                format_int(int(total_affected_value)), align='right'))

        table.add(row)

        message.add(table)

        return message
Beispiel #17
0
    def action_checklist(self):
        """Action checklist for the itb earthquake fatality report.

        :returns: The action checklist
        :rtype: list
        """
        total_fatalities = self.total_fatalities
        total_displaced = self.total_evacuated
        rounded_displaced = format_int(population_rounding(total_displaced))

        fields = super(ITBFatalityFunction, self).action_checklist()
        if total_fatalities:
            fields.append(tr(
                'Are there enough victim identification units available '
                'for %s people?') % (
                    format_int(population_rounding(total_fatalities))))
        if total_displaced:
            fields.append(tr(
                'Are there enough shelters and relief items available for '
                '%s people?') % rounded_displaced)
        if rounded_displaced:
            fields.append(tr(
                'If yes, where are they located and how will we '
                'distribute them?'))
        if total_displaced:
            fields.append(tr(
                'If no, where can we obtain additional relief items '
                'from and how will we transport them?'))

        return fields
Beispiel #18
0
def get_map_title(hazard, exposure, hazard_category):
    """Helper to get map title.

    :param hazard: A hazard definition.
    :type hazard: dict

    :param exposure: An exposure definition.
    :type exposure: dict

    :param hazard_category: A hazard category definition.
    :type hazard_category: dict

    :returns: Map title based on the input.
    :rtype: str
    """
    if hazard == hazard_generic:
        map_title = tr('{exposure_name} affected').format(
            exposure_name=exposure['name'])
    else:
        if hazard_category == hazard_category_single_event:
            map_title = tr(
                '{exposure_name} affected by {hazard_name} event').format(
                    exposure_name=exposure['name'],
                    hazard_name=hazard['name'])
        else:
            map_title = tr(
                '{exposure_name} affected by {hazard_name} hazard').format(
                    exposure_name=exposure['name'],
                    hazard_name=hazard['name'])
    return map_title
Beispiel #19
0
def get_analysis_question(hazard, exposure):
    """Construct analysis question based on hazard and exposure.

    :param hazard: A hazard definition.
    :type hazard: dict

    :param exposure: An exposure definition.
    :type exposure: dict

    :returns: Analysis question based on reporting standards.
    :rtype: str
    """
    # First we look for a translated hardcoded question.
    question = specific_analysis_question(hazard, exposure)
    if question:
        return question

    if hazard == hazard_generic:
        # Secondly, if the hazard is generic, we don't need the hazard.
        question = tr(
            'In each of the hazard zones {exposure_measure} {exposure_name} '
            'might be affected?').format(
            exposure_measure=exposure['measure_question'],
            exposure_name=exposure['name'])
        return question

    # Then, we fallback on a generated string on the fly.
    question = tr(
        'In the event of a {hazard_name}, {exposure_measure} {exposure_name} '
        'might be affected?').format(
            hazard_name=hazard['name'],
            exposure_measure=exposure['measure_question'],
            exposure_name=exposure['name'])
    return question
    def impact_summary_headings(self):
        """Headings for the impact summary.

        :return: Headings
        :rtype: list
        """
        return [tr('Buildings'), tr('Count')]
    def impact_summary(self):
        """Create impact summary as data.

        :returns: Impact Summary in dictionary format.
        :rtype: dict
        """
        affect_types = self._impact_breakdown
        attributes = ['category', 'value']
        fields = []
        for (category, building_breakdown) in self.affected_buildings.items():
            total_affected = [0] * len(affect_types)
            for affected_breakdown in building_breakdown.values():
                for affect_type, number_affected in affected_breakdown.items():
                    count = affect_types.index(affect_type)
                    total_affected[count] += number_affected
            field = [tr(category)]
            for affected in total_affected:
                field.append(affected)
            fields.append(field)

        if len(self._affected_categories) > 1:
            fields.append(
                [tr('Affected buildings'), self.total_affected_buildings])

        if self._affected_categories == self.affected_buildings.keys():
            fields.append([
                tr('Not affected buildings'), self.total_unaffected_buildings]
            )

        fields.append([tr('Total'), self.total_buildings])

        return {
            'attributes': attributes,
            'fields': fields
        }
        def get_metadata():
            """Return metadata as a dictionary.

            This is a static method. You can use it to get the metadata in
            dictionary format for an impact function.

            :returns: A dictionary representing all the metadata for the
                concrete impact function.
            :rtype: dict
            """
            dict_meta = {
                'id': 'FloodNativePolygonExperimentalFunction',
                'name': tr('Flood Native Polygon Experimental Function'),
                'impact': tr('Be-flooded'),
                'author': 'Dmitry Kolesov',
                'date_implemented': 'N/A',
                'overview': tr('N/A'),
                'categories': {
                    'hazard': {
                        'definition': hazard_definition,
                        'subcategories': [hazard_flood],
                        'units': [unit_wetdry],
                        'layer_constraints': [layer_vector_polygon]
                    },
                    'exposure': {
                        'definition': exposure_definition,
                        'subcategories': [exposure_structure],
                        'units': [unit_building_type_type],
                        'layer_constraints': [layer_vector_polygon]
                    }
                }
            }
            return dict_meta
        def get_metadata():
            """Return metadata as a dictionary

            This is a static method. You can use it to get the metadata in
            dictionary format for an impact function.

            :returns: A dictionary representing all the metadata for the
                concrete impact function.
            :rtype: dict
            """
            dict_meta = {
                'id': 'ITBFatalityFunction',
                'name': tr('ITB Fatality Function'),
                'impact': tr('Die or be displaced'),
                'author': 'Hadi Ghasemi',
                'date_implemented': 'N/A',
                'overview': tr(
                    'To assess the impact of earthquake on population based '
                    'on earthquake model developed by ITB'),
                'categories': {
                    'hazard': {
                        'definition': hazard_definition,
                        'subcategories': [hazard_earthquake],
                        'units': [unit_mmi],
                        'layer_constraints': [layer_raster_continuous]
                    },
                    'exposure': {
                        'definition': exposure_definition,
                        'subcategories': [exposure_population],
                        'units': [unit_people_per_pixel],
                        'layer_constraints': [layer_raster_continuous]
                    }
                }
            }
            return dict_meta
    def minimum_needs_breakdown(self):
        """Breakdown by building type.

        :returns: The buildings breakdown report.
        :rtype: list
        """
        minimum_needs_breakdown_report = [{
            'content': tr('Evacuated population minimum needs'),
            'header': True
        }]
        total_needs = self.total_needs
        for frequency, needs in total_needs.items():
            minimum_needs_breakdown_report.append(
                {
                    'content': [
                        tr('Needs that should be provided %s' % frequency),
                        tr('Total')],
                    'header': True
                })
            for resource in needs:
                minimum_needs_breakdown_report.append(
                    {
                        'content': [
                            tr(resource['table name']),
                            tr(format_int(resource['amount']))]
                    })
        return minimum_needs_breakdown_report
    def set_widgets(self):
        """Set widgets on the Aggregation Layer Origin Type tab"""
        # First, list available layers in order to check if there are
        # any available layers. Note This will be repeated in
        # set_widgets_step_fc_agglayer_from_canvas because we need
        # to list them again after coming back from the Keyword Wizard.
        self.parent.step_fc_agglayer_from_canvas.\
            list_compatible_canvas_layers()
        lst_wdg = self.parent.step_fc_agglayer_from_canvas.lstCanvasAggLayers
        if lst_wdg.count():
            self.rbAggLayerFromCanvas.setText(tr(
                'I would like to use an aggregation layer already loaded in '
                'QGIS\n'
                '(launches the %s for aggregation if needed)'
            ) % self.parent.keyword_creation_wizard_name)
            self.rbAggLayerFromCanvas.setEnabled(True)
            self.rbAggLayerFromCanvas.click()
        else:
            self.rbAggLayerFromCanvas.setText(tr(
                'I would like to use an aggregation layer already loaded in '
                'QGIS\n'
                '(no suitable layers found)'))
            self.rbAggLayerFromCanvas.setEnabled(False)
            self.rbAggLayerFromBrowser.click()

        # Set icon
        self.lblIconIFCWAggregationOrigin.setPixmap(QPixmap(None))
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 3.2.2

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    message.add(m.Paragraph(tr(
        'This tool will calculated minimum needs for evacuated people. To '
        'use this tool effectively:'
    )))
    tips = m.BulletedList()
    tips.add(tr(
        'Load a point or polygon layer in QGIS. Typically the layer will '
        'represent administrative districts where people have gone to an '
        'evacuation center.'))
    tips.add(tr(
        'Ensure that the layer has an INTEGER attribute for the number of '
        'displaced people associated with each feature.'
    ))
    tips.add(tr(
        'Use the pick lists below to select the layer and the population '
        'field and then press \'OK\'.'
    ))
    tips.add(tr(
        'A new layer will be added to QGIS after the calculation is '
        'complete. The layer will contain the minimum needs per district '
        '/ administrative boundary.'))
    message.add(tips)
    return message
        def get_metadata():
            """Return metadata as a dictionary.

            This is a static method. You can use it to get the metadata in
            dictionary format for an impact function.

            :returns: A dictionary representing all the metadata for the
                concrete impact function.
            :rtype: dict
            """
            dict_meta = {
                'id': 'FloodEvacuationFunctionVectorHazard',
                'name': tr('Flood Evacuation Function Vector Hazard'),
                'impact': tr('Need evacuation'),
                'author': 'AIFDR',
                'date_implemented': 'N/A',
                'overview': tr(
                    'To assess the impacts of flood inundation '
                    'in vector format on population.'),
                'categories': {
                    'hazard': {
                        'definition': hazard_definition,
                        'subcategories': [hazard_flood],
                        'units': [unit_wetdry],
                        'layer_constraints': [layer_vector_polygon]
                    },
                    'exposure': {
                        'definition': exposure_definition,
                        'subcategories': [exposure_population],
                        'units': [unit_people_per_pixel],
                        'layer_constraints': [layer_raster_continuous]
                    }
                }
            }
            return dict_meta
Beispiel #28
0
    def _affected_categories(self):
        """Overwriting the affected categories, since 'unaffected' are counted.

        :returns: The categories that equal effected.
        :rtype: list
        """
        return [tr('Number Inundated'), tr('Number of Wet Buildings')]
Beispiel #29
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)
Beispiel #30
0
    def notes(self):
        """Return the notes section of the report.

        .. versionadded: 3.2.1

        :return: The notes that should be attached to this impact report.
        :rtype: list
        """
        threshold = self.parameters['min threshold'].value
        hazard = self.hazard.keyword('hazard')
        hazard_terminology = tr('flooded')
        hazard_object = tr('flood')
        if hazard == 'flood':
            # Use flooded
            pass
        elif hazard == 'tsunami':
            hazard_terminology = tr('inundated')
            hazard_object = tr('water')

        fields = [
            tr('Roads are %s when %s levels exceed %.2f m.') %
            (hazard_terminology, hazard_object, threshold),
        ]
        # include any generic exposure specific notes from definitions.py
        fields = fields + self.exposure_notes()
        # include any generic hazard specific notes from definitions.py
        fields = fields + self.hazard_notes()
        return fields
Beispiel #31
0
    def read_from_file(self, filename):
        """Read and unpack vector data.

        It is assumed that the file contains only one layer with the
        pertinent features. Further it is assumed for the moment that
        all geometries are points.

        * A feature is a geometry and a set of attributes.
        * A geometry refers to location and can be point, line, polygon or
          combinations thereof.
        * The attributes or obtained through GetField()

        The full OGR architecture is documented at
        * http://www.gdal.org/ogr/ogr_arch.html
        * http://www.gdal.org/ogr/ogr_apitut.html

        Examples are at
        * danieljlewis.org/files/2010/09/basicpythonmap.pdf
        * http://invisibleroads.com/tutorials/gdal-shapefile-points-save.html
        * http://www.packtpub.com/article/geospatial-data-python-geometry

        Limitation of the Shapefile are documented in
        http://resources.esri.com/help/9.3/ArcGISDesktop/com/Gp_ToolRef/
        geoprocessing_tool_reference/
        geoprocessing_considerations_for_shapefile_output.htm

        :param filename: a fully qualified location to the file
        :type filename: str

        :raises: ReadLayerError
        """

        base_name = os.path.splitext(filename)[0]

        # Look for any keywords
        self.keywords = read_keywords(base_name + '.keywords')

        # FIXME (Ole): Should also look for style file to populate style_info

        # Determine name
        if 'title' in self.keywords:
            title = self.keywords['title']

            # Lookup internationalised title if available
            title = tr(title)

            vector_name = title
        else:
            # Use base_name without leading directories as name
            vector_name = os.path.split(base_name)[-1]

        if self.name is None:
            self.name = vector_name
        self.filename = filename
        self.geometry_type = None  # In case there are no features

        fid = ogr.Open(filename)
        if fid is None:
            msg = 'Could not open %s' % filename
            raise ReadLayerError(msg)

        # Assume that file contains all data in one layer
        msg = 'Only one vector layer currently allowed'
        if fid.GetLayerCount() > 1 and self.sublayer is None:
            msg = ('WARNING: Number of layers in %s are %i. '
                   'Only the first layer will currently be '
                   'used. Specify sublayer when creating '
                   'the Vector if you wish to use a different layer.' %
                   (filename, fid.GetLayerCount()))
            LOGGER.warn(msg)
            # Why do we raise an exception if it is only a warning? TS
            raise ReadLayerError(msg)

        if self.sublayer is not None:
            layer = fid.GetLayerByName(self.sublayer)
        else:
            layer = fid.GetLayerByIndex(0)

        # Get spatial extent
        self.extent = layer.GetExtent()

        # Get projection
        p = layer.GetSpatialRef()
        self.projection = Projection(p)

        layer.ResetReading()

        # Extract coordinates and attributes for all features
        geometry = []
        data = []
        # Use feature iterator
        for feature in layer:
            # Record coordinates ordered as Longitude, Latitude
            G = feature.GetGeometryRef()
            if G is None:
                msg = ('Geometry was None in filename %s ' % filename)
                raise ReadLayerError(msg)
            else:
                self.geometry_type = G.GetGeometryType()
                if self.is_point_data:
                    geometry.append((G.GetX(), G.GetY()))
                elif self.is_line_data:
                    ring = get_ring_data(G)
                    geometry.append(ring)
                elif self.is_polygon_data:
                    polygon = get_polygon_data(G)
                    geometry.append(polygon)
                elif self.is_multi_polygon_data:
                    try:
                        G = ogr.ForceToPolygon(G)
                    except:
                        msg = ('Got geometry type Multipolygon (%s) for '
                               'filename %s and could not convert it to '
                               'singlepart. However, you can use QGIS '
                               'functionality to convert multipart vector '
                               'data to singlepart (Vector -> Geometry Tools '
                               '-> Multipart to Singleparts and use the '
                               'resulting dataset.' %
                               (ogr.wkbMultiPolygon, filename))
                        raise ReadLayerError(msg)
                    else:
                        # Read polygon data as single part
                        self.geometry_type = ogr.wkbPolygon
                        polygon = get_polygon_data(G)
                        geometry.append(polygon)
                else:
                    msg = ('Only point, line and polygon geometries are '
                           'supported. '
                           'Geometry type in filename %s '
                           'was %s.' % (filename, self.geometry_type))
                    raise ReadLayerError(msg)

            # Record attributes by name
            number_of_fields = feature.GetFieldCount()
            fields = {}
            for j in range(number_of_fields):
                name = feature.GetFieldDefnRef(j).GetName()

                # FIXME (Ole): Ascertain the type of each field?
                #              We need to cast each appropriately?
                #              This is issue #66
                #              (https://github.com/AIFDR/riab/issues/66)
                # feature_type = feature.GetFieldDefnRef(j).GetType()
                fields[name] = feature.GetField(j)

                # We do this because there is NaN problem on windows
                # NaN value must be converted to _pseudo_in to solve the
                # problem. But, when InaSAFE read the file, it'll be
                # converted back to NaN value, so that NaN in InaSAFE is a
                # numpy.nan
                # please check https://github.com/AIFDR/inasafe/issues/269
                # for more information
                if fields[name] == _pseudo_inf:
                    fields[name] = float('nan')
                # print 'Field', name, feature_type, j, fields[name]

            data.append(fields)
        # Store geometry coordinates as a compact numeric array
        self.geometry = geometry
        self.data = data
Beispiel #32
0
    def save_scenario(self, scenario_file_path=None):
        """Save current scenario to a text file.

        You can use the saved scenario with the batch runner.

        :param scenario_file_path: A path to the scenario file.
        :type scenario_file_path: str
        """
        # Validate Input
        warning_title = tr('InaSAFE Save Scenario Warning')
        is_valid, warning_message = self.validate_input()
        if not is_valid:
            # noinspection PyCallByClass,PyTypeChecker,PyArgumentList
            QMessageBox.warning(self, warning_title, warning_message)
            return

        # Make extent to look like:
        # 109.829170982, -8.13333290561, 111.005344795, -7.49226294379

        # Added in 2.2 to support user defined analysis extents
        if self.dock.extent.user_extent is not None \
                and self.dock.extent.crs is not None:
            # In V4.0, user_extent is QgsGeometry.
            user_extent = self.dock.extent.user_extent.boundingBox()
            extent = extent_to_array(user_extent, self.dock.extent.crs)
        else:
            extent = viewport_geo_array(self.iface.mapCanvas())
        extent_string = ', '.join(('%f' % x) for x in extent)

        exposure_path = self.exposure_layer.source()
        hazard_path = self.hazard_layer.source()
        title = self.keyword_io.read_keywords(self.hazard_layer, 'title')
        title = tr(title)
        default_filename = title.replace(' ',
                                         '_').replace('(',
                                                      '').replace(')', '')

        # Popup a dialog to request the filename if scenario_file_path = None
        dialog_title = tr('Save Scenario')
        if scenario_file_path is None:
            # noinspection PyCallByClass,PyTypeChecker
            scenario_file_path, __ = QFileDialog.getSaveFileName(
                self, dialog_title,
                os.path.join(self.output_directory, default_filename + '.txt'),
                "Text files (*.txt)")
        if scenario_file_path is None or scenario_file_path == '':
            return
        self.output_directory = os.path.dirname(scenario_file_path)

        #  Write to file
        parser = ConfigParser()
        parser.add_section(title)
        # Relative path is not recognized by the batch runner, so we use
        # absolute path.
        parser.set(title, 'exposure', exposure_path)
        parser.set(title, 'hazard', hazard_path)

        parser.set(title, 'extent', extent_string)
        if self.dock.extent.crs is None:
            parser.set(title, 'extent_crs', 'EPSG:4326')
        else:
            parser.set(title, 'extent_crs', self.dock.extent.crs.authid())
        if self.aggregation_layer is not None:
            aggregation_path = self.aggregation_layer.source()
            relative_aggregation_path = self.relative_path(
                scenario_file_path, aggregation_path)
            parser.set(title, 'aggregation', relative_aggregation_path)

        # noinspection PyBroadException
        try:
            of = open(scenario_file_path, 'a')
            parser.write(of)
            of.close()
        except Exception as e:
            # noinspection PyTypeChecker,PyCallByClass,PyArgumentList
            QMessageBox.warning(
                self, 'InaSAFE',
                tr('Failed to save scenario to {path}, exception '
                   '{exception}').format(path=scenario_file_path,
                                         exception=str(e)))
        finally:
            of.close()

        # Save State
        self.save_state()
Beispiel #33
0
# coding=utf-8
"""Definitions relating to API that used in Peta Bencana downloader."""
from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'bd00bfeac510722b427544b186bfa10861749e51'

development_api = {
    'key':
    'development_api',
    'name':
    tr('Development API'),
    'url':
    'https://data-dev.petabencana.id/floods'
    '?city={city_code}&geoformat=geojson&format=json&minimum_state=1',
    'help_url':
    'https://docs.petabencana.id/',
    'available_data': [{
        'code': 'jbd',
        'name': 'Jabodetabek'
    }, {
        'code': 'bdg',
        'name': 'Bandung'
    }, {
        'code': 'sby',
        'name': 'Surabaya'
    }]
}
    def _value_maps_row(value_maps_keyword):
        """Helper to make a message row from a value maps.

        Expected keywords:
        'value_maps': {
            'structure': {
                'ina_structure_flood_hazard_classification': {
                    'classes': {
                        'low': [1, 2, 3],
                        'medium': [4],
                        'high': [5, 6]
                    },
                    'active': True
                },
                'ina_structure_flood_hazard_4_class_classification':
                {
                    'classes': {
                        'low': [1],
                        'medium': [2, 3, 4],
                        'high': [5, 6, 7],
                        'very_high': [8]
                    },
                    'active': False

                }
            },
            'population': {
                'ina_population_flood_hazard_classification': {
                    'classes': {
                        'low': [1],
                        'medium': [2, 3],
                        'high': [4, 5, 6]
                    },
                    'active': False
                },
                'ina_population_flood_hazard_4_class_classification':
                {
                    'classes': {
                        'low': [1, 2],
                        'medium': [3, 4],
                        'high': [4, 5, 6],
                        'very_high': [6, 7, 8]
                    },
                    'active': True
                }
            },
        }

        :param value_maps_keyword: Value of the keyword to be rendered. This
            must be a string representation of a dict, or a dict.
        :type value_maps_keyword: basestring, dict

        :returns: A table to be added into a cell in the keywords table.
        :rtype: safe.messaging.items.table
        """
        if isinstance(value_maps_keyword, basestring):
            value_maps_keyword = literal_eval(value_maps_keyword)

        table = m.Table(style_class='table table-condensed table-striped')

        i = 0
        for exposure_key, classifications in value_maps_keyword.items():
            i += 1
            exposure = definition(exposure_key)
            exposure_row = m.Row()
            exposure_row.add(m.Cell(m.ImportantText(tr('Exposure'))))
            exposure_row.add(m.Cell(exposure['name']))
            table.add(exposure_row)

            classification_row = m.Row()
            classification_row.add(m.Cell(m.ImportantText(tr(
                'Classification'))))
            active_classification = None
            for classification, value in classifications.items():
                if value.get('active'):
                    active_classification = definition(classification)
                    if active_classification.get('name'):
                        classification_row.add(
                            m.Cell(active_classification['name']))
                    break

            if not active_classification:
                classification_row.add(m.Cell(tr('No classifications set.')))
                continue
            table.add(classification_row)

            header = m.Row()
            header.add(m.Cell(tr('Class name')))
            header.add(m.Cell(tr('Values')))
            table.add(header)
            classes = active_classification.get('classes')
            # Sort by value, put the lowest first
            classes = sorted(classes, key=lambda k: k['value'])
            for the_class in classes:
                value_map = classifications[active_classification['key']][
                    'classes'].get(the_class['key'], [])
                row = m.Row()
                row.add(m.Cell(the_class['name']))
                row.add(m.Cell(', '.join([str(v) for v in value_map])))
                table.add(row)

            if i < len(value_maps_keyword):
                # Empty row
                empty_row = m.Row()
                empty_row.add(m.Cell(''))
                empty_row.add(m.Cell(''))
                table.add(empty_row)

        return table
    def _threshold_to_row(thresholds_keyword):
        """Helper to make a message row from a threshold

        We are expecting something like this:

        {
            'thresholds': {
                'structure': {
                    'ina_structure_flood_hazard_classification': {
                        'classes': {
                            'low': [1, 2],
                            'medium': [3, 4],
                            'high': [5, 6]
                        },
                        'active': True
                    },
                    'ina_structure_flood_hazard_4_class_classification':
                    {
                        'classes': {
                            'low': [1, 2],
                            'medium': [3, 4],
                            'high': [5, 6],
                            'very_high': [7, 8]
                        },
                        'active': False

                    }
                },
                'population': {
                    'ina_population_flood_hazard_classification': {
                        'classes': {
                            'low': [1, 2.5],
                            'medium': [2.5, 4.5],
                            'high': [4.5, 6]
                        },
                        'active': False
                    },
                    'ina_population_flood_hazard_4_class_classification':
                    {
                        'classes': {
                            'low': [1, 2.5],
                            'medium': [2.5, 4],
                            'high': [4, 6],
                            'very_high': [6, 8]
                        },
                        'active': True
                    }
                },
            },

        Each value is a list with exactly two element [a, b], where a <= b.

        :param thresholds_keyword: Value of the keyword to be rendered. This
            must be a string representation of a dict, or a dict.
        :type thresholds_keyword: basestring, dict

        :returns: A table to be added into a cell in the keywords table.
        :rtype: safe.messaging.items.table
        """
        if isinstance(thresholds_keyword, basestring):
            thresholds_keyword = literal_eval(thresholds_keyword)

        for k, v in thresholds_keyword.items():
            # If the v is not dictionary, it should be the old value maps.
            # To handle thresholds in the Impact Function.
            if not isinstance(v, dict):
                table = m.Table(style_class='table table-condensed')

                for key, value in thresholds_keyword.items():
                    row = m.Row()
                    name = definition(key)['name'] if definition(key) else key
                    row.add(m.Cell(m.ImportantText(name)))
                    pretty_value = tr('%s to %s' % (value[0], value[1]))
                    row.add(m.Cell(pretty_value))

                    table.add(row)
                return table

        table = m.Table(style_class='table table-condensed table-striped')

        i = 0
        for exposure_key, classifications in thresholds_keyword.items():
            i += 1
            exposure = definition(exposure_key)
            exposure_row = m.Row()
            exposure_row.add(m.Cell(m.ImportantText('Exposure')))
            exposure_row.add(m.Cell(m.Text(exposure['name'])))
            exposure_row.add(m.Cell(''))
            table.add(exposure_row)

            active_classification = None
            classification_row = m.Row()
            classification_row.add(m.Cell(m.ImportantText('Classification')))
            for classification, value in classifications.items():
                if value.get('active'):
                    active_classification = definition(classification)
                    classification_row.add(
                        m.Cell(active_classification['name']))
                    classification_row.add(m.Cell(''))
                    break

            if not active_classification:
                classification_row.add(m.Cell(tr('No classifications set.')))
                classification_row.add(m.Cell(''))
                continue

            table.add(classification_row)

            header = m.Row()
            header.add(m.Cell(tr('Class name')))
            header.add(m.Cell(tr('Minimum')))
            header.add(m.Cell(tr('Maximum')))
            table.add(header)
            classes = active_classification.get('classes')
            # Sort by value, put the lowest first
            classes = sorted(classes, key=lambda k: k['value'])
            for the_class in classes:
                threshold = classifications[active_classification['key']][
                    'classes'][the_class['key']]
                row = m.Row()
                row.add(m.Cell(the_class['name']))
                row.add(m.Cell(threshold[0]))
                row.add(m.Cell(threshold[1]))
                table.add(row)

            if i < len(thresholds_keyword):
                # Empty row
                empty_row = m.Row()
                empty_row.add(m.Cell(''))
                empty_row.add(m.Cell(''))
                table.add(empty_row)

        return table
    def _keyword_to_row(self, keyword, value, wrap_slash=False):
        """Helper to make a message row from a keyword.

        .. versionadded:: 3.2

        Use this when constructing a table from keywords to display as
        part of a message object.

        :param keyword: The keyword to be rendered.
        :type keyword: str

        :param value: Value of the keyword to be rendered.
        :type value: basestring

        :param wrap_slash: Whether to replace slashes with the slash plus the
            html <wbr> tag which will help to e.g. wrap html in small cells if
            it contains a long filename. Disabled by default as it may cause
            side effects if the text contains html markup.
        :type wrap_slash: bool

        :returns: A row to be added to a messaging table.
        :rtype: safe.messaging.items.row.Row
        """
        row = m.Row()
        # Translate titles explicitly if possible
        if keyword == 'title':
            value = tr(value)
        # # See #2569
        if keyword == 'url':
            if isinstance(value, QUrl):
                value = value.toString()
        if keyword == 'date':
            if isinstance(value, QDateTime):
                value = value.toString('d MMM yyyy')
            elif isinstance(value, datetime):
                value = value.strftime('%d %b %Y')
        # we want to show the user the concept name rather than its key
        # if possible. TS
        keyword_definition = definition(keyword)
        if keyword_definition is None:
            keyword_definition = tr(keyword.capitalize().replace(
                '_', ' '))
        else:
            try:
                keyword_definition = keyword_definition['name']
            except KeyError:
                # Handling if name is not exist.
                keyword_definition = keyword_definition['key'].capitalize()
                keyword_definition = keyword_definition.replace('_', ' ')

        # We deal with some special cases first:

        # In this case the value contains a DICT that we want to present nicely
        if keyword in [
                'value_map',
                'inasafe_fields',
                'inasafe_default_values']:
            value = self._dict_to_row(value)
        elif keyword == 'value_maps':
            value = self._value_maps_row(value)
        elif keyword == 'thresholds':
            value = self._threshold_to_row(value)
        # In these KEYWORD cases we show the DESCRIPTION for
        # the VALUE keyword_definition
        elif keyword in ['classification']:
            # get the keyword_definition for this class from definitions
            value = definition(value)
            value = value['description']
        # In these VALUE cases we show the DESCRIPTION for
        # the VALUE keyword_definition
        elif value in []:
            # get the keyword_definition for this class from definitions
            value = definition(value)
            value = value['description']
        # In these VALUE cases we show the NAME for the VALUE
        # keyword_definition
        elif value in [
                'multiple_event',
                'single_event',
                'point',
                'line',
                'polygon'
                'field']:
            # get the name for this class from definitions
            value = definition(value)
            value = value['name']
        # otherwise just treat the keyword as literal text
        else:
            # Otherwise just directly read the value
            value = get_string(value)

        key = m.ImportantText(keyword_definition)
        row.add(m.Cell(key))
        row.add(m.Cell(value, wrap_slash=wrap_slash))
        return row
    def to_message(self, keywords=None, show_header=True):
        """Format keywords as a message object.

        .. versionadded:: 3.2

        .. versionchanged:: 3.3 - default keywords to None

        The message object can then be rendered to html, plain text etc.

        :param keywords: Keywords to be converted to a message. Optional. If
            not passed then we will attempt to get keywords from self.layer
            if it is not None.
        :type keywords: dict

        :param show_header: Flag indicating if InaSAFE logo etc. should be
            added above the keywords table. Default is True.
        :type show_header: bool

        :returns: A safe message object containing a table.
        :rtype: safe.messaging.Message
        """
        if keywords is None and self.layer is not None:
            keywords = self.read_keywords(self.layer)
        # This order was determined in issue #2313
        preferred_order = [
            'title',
            'layer_purpose',
            'exposure',
            'hazard',
            'hazard_category',
            'layer_geometry',
            'layer_mode',
            'classification',
            'exposure_unit',
            'continuous_hazard_unit',
            'value_map',  # attribute values
            'thresholds',  # attribute values
            'value_maps',  # attribute values
            'inasafe_fields',
            'inasafe_default_values',
            'resample',
            'source',
            'url',
            'scale',
            'license',
            'date',
            'keyword_version'
        ]  # everything else in arbitrary order
        report = m.Message()
        if show_header:
            logo_element = m.Brand()
            report.add(logo_element)
            report.add(m.Heading(tr(
                'Layer keywords:'), **styles.BLUE_LEVEL_4_STYLE))
            report.add(m.Text(tr(
                'The following keywords are defined for the active layer:')))

        table = m.Table(style_class='table table-condensed table-striped')
        # First render out the preferred order keywords
        for keyword in preferred_order:
            if keyword in keywords:
                value = keywords[keyword]
                row = self._keyword_to_row(keyword, value)
                keywords.pop(keyword)
                table.add(row)

        # now render out any remaining keywords in arbitrary order
        for keyword in keywords:
            value = keywords[keyword]
            row = self._keyword_to_row(keyword, value)
            table.add(row)

        # If the keywords class was instantiated with a layer object
        # we can add some context info not stored in the keywords themselves
        # but that is still useful to see...
        if self.layer:
            # First the CRS
            keyword = tr('Reference system')
            value = self.layer.crs().authid()
            row = self._keyword_to_row(keyword, value)
            table.add(row)
            # Next the data source
            keyword = tr('Layer source')
            value = self.layer.source()
            row = self._keyword_to_row(keyword, value, wrap_slash=True)
            table.add(row)

        # Finalise the report
        report.add(table)
        return report
Beispiel #38
0
__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'bd00bfeac510722b427544b186bfa10861749e51'

##
# For QGIS < 2.18.13 and QGIS < 2.14.19, docstrings are used in the QGIS GUI
# in the Expression dialog and also in the InaSAFE Help dialog.
#
# For QGIS >= 2.18.13, QGIS >= 2.14.19 and QGIS 3, the translated variable will
# be used in QGIS.
# help_text is used for QGIS 2.18 and 2.14
# helpText is used for QGIS 3 : https://github.com/qgis/QGIS/pull/5059
##

description = tr(
    'Retrieve a value from a field in the analysis summary layer.')
examples = {
    'inasafe_analysis_summary_field_value(\'total_not_exposed\')': 3
}
help_message = generate_expression_help(description, examples)


@qgsfunction(
    args='auto', group='InaSAFE', usesGeometry=False, referencedColumns=[],
    help_text=help_message.to_html(), helpText=help_message.to_html())
def inasafe_analysis_summary_field_value(field, feature, parent):
    """Retrieve a value from a field in the analysis summary layer.

    e.g. inasafe_analysis_summary_field_value('total_not_exposed') -> 3
    """
    _ = feature, parent  # NOQA
    def action_checklist(self):
        """Polygon Population action.

        :returns: The population breakdown report.
        :rtype: safe.messaging.Message
        """
        message = m.Message(style_class='container')
        message.add(m.Heading(tr('Action checklist'), **styles.INFO_STYLE))
        population = population_rounding(sum(
            self.affected_population.values()))
        checklist = m.BulletedList()
        checklist.add(tr('Which group or people is most affected?'))
        checklist.add(
            tr('Who are the vulnerable people in the population and why?'))
        checklist.add(tr('How will warnings be disseminated?'))
        checklist.add(tr('What are people\'s likely movements?'))
        checklist.add(
            tr('What are the security factors for the affected people?'))
        checklist.add(
            tr('What are the security factors for relief responders?'))
        checklist.add(tr('How will we reach evacuated people?'))
        checklist.add(
            tr('What kind of food does the people normally consume?'))
        checklist.add(
            tr('What are the critical non-food items required by the affected '
               'people?'))
        checklist.add(
            tr('Are there enough water supply, sanitation, hygiene, food, '
               'shelter, medicines and relief items available for %s people?' %
               format_int(population)))

        checklist.add(
            tr('If yes, where are they located and how will we distribute them?'
               ))
        checklist.add(
            tr('If no, where can we obtain additional relief items and how will '
               'we distribute them?'))
        checklist.add(tr('What are the related health risks?'))
        checklist.add(
            tr('Who are the key people responsible for coordination?'))
        message.add(checklist)

        return message
Beispiel #40
0
# coding=utf-8
"""Wizard Strings."""

from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'e96e916695a1f1d3ab34117194b58dc73d7743a4'

category_question = tr(
    'By following the simple steps in this wizard, you can assign '
    'keywords to your layer: <b>%s</b>. First you need to define the purpose '
    'of your layer. Is it a <b>hazard</b>, <b>exposure</b>, or '
    '<b>aggregation</b> layer? ')  # (layer name)
category_question_hazard = tr(
    'You have selected a layer that needs to have keywords assigned or '
    'updated. In the next steps you can assign keywords to that layer. '
    'First you need to confirm the layer is a <b>hazard</b> layer.')
category_question_exposure = tr(
    'You have selected a layer that needs to have keywords assigned or '
    'updated. In the next steps you can assign keywords to that layer. '
    'First you need to confirm the layer is an <b>exposure</b>.')
category_question_aggregation = tr(
    'You have selected a layer that needs to have keywords assigned or '
    'updated. In the next steps you can assign keywords to that layer. '
    'First you need to confirm the layer is an <b>aggregation</b> layer.')
hazard_category_question = tr(
    'What type of <b>hazard scenario</b> does this layer represent? '
    '<p>Does it represent a <b>single event</b> or <b>multiple events</b>?'
    '</p>')
Beispiel #41
0
        message = tr('No keywords file found for %s' % keyword_file_path)
        raise NoKeywordsFoundError(message)

    # now get the requested keyword using the inasafe library
    try:
        dictionary = read_keywords(keyword_file_path)
    except Exception, e:
        message = tr('Keyword retrieval failed for %s (%s) \n %s' %
                     (keyword_file_path, keyword, str(e)))
        raise KeywordNotFoundError(message)

    # if no keyword was supplied, just return the dict
    if keyword is None:
        return dictionary
    if keyword not in dictionary:
        message = tr('No value was found in file %s for keyword %s' %
                     (keyword_file_path, keyword))
        raise KeywordNotFoundError(message)

    try:
        value = dictionary[keyword]
    except:
        raise
    return value


def write_keywords_to_file(filename, keywords):
    """Thin wrapper around the safe write_keywords function.

    :param filename:  Path to layer that must be written.
          If the file does not end in .keywords, its extension will be
          stripped off and the basename + .keywords will be used as the file.
Beispiel #42
0
def run_single_post_processor(layer, post_processor):
    """Run single post processor.

    If the layer has the output field, it will pass the post
    processor calculation.

    :param layer: The vector layer to use for post processing.
    :type layer: QgsVectorLayer

    :param post_processor: A post processor definition.
    :type post_processor: dict

    :returns: Tuple with True if success, else False with an error message.
    :rtype: (bool, str)
    """
    if not layer.editBuffer():

        # Turn on the editing mode.
        if not layer.startEditing():
            msg = tr('The impact layer could not start the editing mode.')
            return False, msg

    # Calculate based on formula
    # Iterate all possible output and create the correct field.
    for output_key, output_value in post_processor['output'].items():

        # Get output attribute name
        key = output_value['value']['key']
        output_field_name = output_value['value']['field_name']
        layer.keywords['inasafe_fields'][key] = output_field_name

        # If there is already the output field, don't proceed
        if layer.fieldNameIndex(output_field_name) > -1:
            msg = tr('The field name %s already exists.' % output_field_name)
            layer.rollBack()
            return False, msg

        # Add output attribute name to the layer
        field = create_field_from_definition(output_value['value'])
        result = layer.addAttribute(field)
        if not result:
            msg = tr('Error while creating the field %s.' % output_field_name)
            layer.rollBack()
            return False, msg

        # Get the index of output attribute
        output_field_index = layer.fieldNameIndex(output_field_name)

        if layer.fieldNameIndex(output_field_name) == -1:
            msg = tr('The field name %s has not been created.' %
                     output_field_name)
            layer.rollBack()
            return False, msg

        # Get the input field's indexes for input
        input_indexes = {}

        input_properties = {}

        # Default parameters
        default_parameters = {}

        msg = None

        # Iterate over every inputs.
        for key, values in post_processor['input'].items():
            values = values if isinstance(values, list) else [values]
            for value in values:
                is_constant_input = (value['type'] == constant_input_type)
                is_field_input = (value['type'] == field_input_type
                                  or value['type'] == dynamic_field_input_type)
                is_geometry_input = (
                    value['type'] == geometry_property_input_type)
                is_keyword_input = (value['type'] == keyword_input_type)
                is_needs_input = (value['type'] == needs_profile_input_type)
                is_layer_property_input = (
                    value['type'] == layer_property_input_type)
                if value['type'] == keyword_value_expected:
                    break
                if is_constant_input:
                    default_parameters[key] = value['value']
                    break
                elif is_field_input:
                    if value['type'] == dynamic_field_input_type:
                        key_template = value['value']['key']
                        field_param = value['field_param']
                        field_key = key_template % field_param
                    else:
                        field_key = value['value']['key']

                    inasafe_fields = layer.keywords['inasafe_fields']
                    name_field = inasafe_fields.get(field_key)

                    if not name_field:
                        msg = tr('%s has not been found in inasafe fields.' %
                                 value['value']['key'])
                        continue

                    index = layer.fieldNameIndex(name_field)

                    if index == -1:
                        fields = layer.fields().toList()
                        msg = tr('The field name %s has not been found in %s' %
                                 (name_field, [f.name() for f in fields]))
                        continue

                    input_indexes[key] = index
                    break

                # For geometry, create new field that contain the value
                elif is_geometry_input:
                    input_properties[key] = geometry_property_input_type['key']
                    break

                # for keyword
                elif is_keyword_input:
                    # See http://stackoverflow.com/questions/14692690/
                    # access-python-nested-dictionary-items-via-a-list-of-keys
                    value = reduce(lambda d, k: d[k], value['value'],
                                   layer.keywords)

                    default_parameters[key] = value
                    break

                # for needs profile
                elif is_needs_input:
                    need_parameter = minimum_needs_parameter(
                        parameter_name=value['value'])
                    value = need_parameter.value

                    default_parameters[key] = value
                    break

                # for layer property
                elif is_layer_property_input:
                    if value['value'] == layer_crs_input_value:
                        default_parameters[key] = layer.crs()

                    if value['value'] == size_calculator_input_value:
                        exposure = layer.keywords.get('exposure')
                        if not exposure:
                            keywords = layer.keywords.get('exposure_keywords')
                            exposure = keywords.get('exposure')

                        default_parameters[key] = SizeCalculator(
                            layer.crs(), layer.geometryType(), exposure)
                    break

            else:
                # executed when we can't find all the inputs
                layer.rollBack()
                return False, msg

        # Create iterator for feature
        request = QgsFeatureRequest().setSubsetOfAttributes(
            input_indexes.values())
        iterator = layer.getFeatures(request)

        inputs = input_indexes.copy()
        inputs.update(input_properties)

        # Iterate all feature
        for feature in iterator:
            attributes = feature.attributes()

            # Create dictionary to store the input
            parameters = {}
            parameters.update(default_parameters)

            # Fill up the input from fields
            for key, value in inputs.items():
                if value == geometry_property_input_type['key']:
                    parameters[key] = feature.geometry()
                else:
                    parameters[key] = attributes[value]
            # Fill up the input from geometry property

            # Evaluate the function
            python_function = output_value.get('function')
            if python_function:
                # Launch the python function
                post_processor_result = python_function(**parameters)
            else:
                # Evaluate the function
                formula = output_value['formula']
                post_processor_result = evaluate_formula(formula, parameters)

            # The affected postprocessor returns a boolean.
            if isinstance(post_processor_result, bool):
                post_processor_result = tr(unicode(post_processor_result))

            layer.changeAttributeValue(feature.id(), output_field_index,
                                       post_processor_result)

    layer.commitChanges()
    return True, None
Beispiel #43
0
    def populate_parameter(self):
        """Helper to setup the parameter widget."""
        used_fields = []
        self.parameters = []
        for field in self.field_group.get('fields', []):
            selected_option = DO_NOT_REPORT
            options = OrderedDict([
                (DO_NOT_REPORT, {
                    'label': tr('Do not report'),
                    'value': None,
                    'type': STATIC,
                    'constraint': {}
                }),
            ])

            # Example: count
            if field['absolute']:
                # Used in field options
                field_label = tr('Count fields')
            else:  # Example: ratio
                # Used in field options
                field_label = tr('Ratio fields')
                global_default_value = get_inasafe_default_value_qsetting(
                    self.setting, GLOBAL, field['key'])
                options[GLOBAL_DEFAULT] = {
                    'label': tr('Global default'),
                    'value': global_default_value,
                    'type': STATIC,
                    'constraint': {}
                }
                default_custom_value = get_inasafe_default_value_qsetting(
                    self.setting, RECENT, field['key'])
                custom_value = self.metadata.get('inasafe_default_values',
                                                 {}).get(
                                                     field['key'],
                                                     default_custom_value)
                if field['key'] in self.metadata.get('inasafe_default_values',
                                                     {}):
                    if custom_value == global_default_value:
                        selected_option = GLOBAL_DEFAULT
                    else:
                        selected_option = CUSTOM_VALUE
                min_value = field['default_value'].get('min_value', 0)
                max_value = field['default_value'].get('max_value', 100)
                default_step = (max_value - min_value) / 100.0
                step = field['default_value'].get('increment', default_step)
                options[CUSTOM_VALUE] = {
                    'label': tr('Custom'),
                    'value': custom_value,
                    'type': SINGLE_DYNAMIC,
                    'constraint': {
                        'min': min_value,
                        'max': max_value,
                        'step': step
                    }
                }

            custom_fields = self.metadata.get('inasafe_fields',
                                              {}).get(field['key'], [])
            if field['key'] in self.metadata.get('inasafe_fields', {}):
                selected_option = FIELDS
            if isinstance(custom_fields, str):
                custom_fields = [custom_fields]
            options[FIELDS] = {
                'label': field_label,
                'value': custom_fields,
                'type': MULTIPLE_DYNAMIC,
                'constraint': {}
            }
            used_fields.extend(custom_fields)

            parameter = GroupSelectParameter()
            parameter.guid = field['key']
            parameter.name = field['name']
            parameter.options = options
            parameter.selected = selected_option
            parameter.help_text = field['help_text']
            parameter.description = field['description']

            self.parameters.append(parameter)

        self.parameter_container = ParameterContainer(
            parameters=self.parameters,
            extra_parameters=self.extra_parameters,
            vertical=False)
        self.parameter_container.setup_ui()

        constraints = self.field_group.get('constraints', {})

        for key, value in list(constraints.items()):
            self.parameter_container.add_validator(
                validators[key],
                kwargs=value['kwargs'],
                validation_message=value['message'])

        self.parameter_layout.addWidget(self.parameter_container)

        default_ratio_help_text = tr(
            'By default, InaSAFE will calculate the default ratio '
            'however users have the option to include this in the '
            'analysis report. If you do not want to see the default '
            'results in the report choose "do not report".')
        # Set move or copy
        if self.field_group.get('exclusive', False):
            # If exclusive, do not add used field.
            self.populate_field_list(excluded_fields=used_fields)
            # Use move action since it's exclusive
            self.field_list.setDefaultDropAction(Qt.MoveAction)
            # Just make sure that the signal is disconnected
            try:
                # noinspection PyUnresolvedReferences
                self.field_list.itemChanged.disconnect(self.drop_remove)
            except TypeError:
                pass
            # Set header
            header_text = self.field_group['description']
            header_text += '\n\n' + default_ratio_help_text
            header_text += '\n\n' + tr(
                'You can only map one field to one concept.')
        else:
            # If not exclusive, add all field.
            self.populate_field_list()
            # Use copy action since it's not exclusive
            self.field_list.setDefaultDropAction(Qt.CopyAction)
            # noinspection PyUnresolvedReferences
            self.field_list.itemChanged.connect(
                partial(self.drop_remove, field_list=self.field_list))
            self.connect_drop_remove_parameter()
            # Set header
            header_text = self.field_group['description']
            header_text += '\n\n' + default_ratio_help_text
            header_text += '\n\n' + tr(
                'You can map one field to more than one concepts.')

        self.header_label.setText(header_text)
Beispiel #44
0
def impact_attribution(keywords, inasafe_flag=False):
    """Make a little table for attribution of data sources used in impact.

    :param keywords: A keywords dict for an impact layer.
    :type keywords: dict

    :param inasafe_flag: bool - whether to show a little InaSAFE promotional
        text in the attribution output. Defaults to False.

    :returns: An html snippet containing attribution information for the impact
        layer. If no keywords are present or no appropriate keywords are
        present, None is returned.
    :rtype: safe.messaging.Message
    """
    if keywords is None:
        return None

    join_words = ' - %s ' % tr('sourced from')
    hazard_details = tr('Hazard details')
    hazard_title_keywords = 'hazard_title'
    hazard_source_keywords = 'hazard_source'
    exposure_details = tr('Exposure details')
    exposure_title_keywords = 'exposure_title'
    exposure_source_keyword = 'exposure_source'

    if hazard_title_keywords in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        hazard_title = tr(keywords[hazard_title_keywords])
    else:
        hazard_title = tr('Hazard layer')

    if hazard_source_keywords in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        hazard_source = tr(keywords[hazard_source_keywords])
    else:
        hazard_source = tr('an unknown source')

    if exposure_title_keywords in keywords:
        exposure_title = keywords[exposure_title_keywords]
    else:
        exposure_title = tr('Exposure layer')

    if exposure_source_keyword in keywords:
        exposure_source = keywords[exposure_source_keyword]
    else:
        exposure_source = tr('an unknown source')

    report = m.Message()
    report.add(m.Heading(hazard_details, **INFO_STYLE))
    report.add(m.Paragraph(hazard_title, join_words, hazard_source))

    report.add(m.Heading(exposure_details, **INFO_STYLE))
    report.add(m.Paragraph(exposure_title, join_words, exposure_source))

    if inasafe_flag:
        report.add(m.Heading(tr('Software notes'), **INFO_STYLE))
        # noinspection PyUnresolvedReferences
        inasafe_phrase = tr(
            'This report was created using InaSAFE version %s. Visit '
            'http://inasafe.org to get your free copy of this software!'
            'InaSAFE has been jointly developed by BNPB, AusAid/AIFDRR & the '
            'World Bank') % (get_version())

        report.add(m.Paragraph(m.Text(inasafe_phrase)))
    return report
Beispiel #45
0
# coding=utf-8
"""Definitions relating to default value."""

from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'bc3796555162c60f37041c9b01cea5ad77504d39'

female_ratio_default_value = {
    'key':
    'female_ratio_default_value',
    'name':
    tr('Female Ratio Global Default'),
    # https://www.cia.gov/library/publications/the-world-factbook/geos/xx.html
    # total population: 1.01 male(s)/female (2011 est.)
    'default_value':
    0.496,
    # Updated for InaSAFE 4.1
    # UNSD World Data, 2010.
    'min_value':
    0,
    'max_value':
    1,
    'increment':
    0.001,
    'description':
    tr('Default ratio of females per 100 people in the total population.')
}
Beispiel #46
0
    def __init__(self, field_group=None, parent=None, iface=None):
        """Constructor."""
        # Init from parent class
        QWidget.__init__(self, parent)

        # Attributes
        self.layer = None
        self.metadata = {}
        self.parent = parent
        self.iface = iface
        self.field_group = field_group
        self.setting = QSettings()  # TODO(IS): Make dynamic

        # Main container
        self.main_layout = QVBoxLayout()

        # Inner layout
        self.header_layout = QHBoxLayout()
        self.content_layout = QHBoxLayout()
        self.footer_layout = QHBoxLayout()

        # Header
        self.header_label = QLabel()
        self.header_label.setWordWrap(True)

        # Content
        self.field_layout = QVBoxLayout()
        self.parameter_layout = QHBoxLayout()

        self.field_description = QLabel(tr('List of fields'))

        self.field_list = QListWidget()
        self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.field_list.setDragDropMode(QAbstractItemView.DragDrop)
        self.field_list.setDefaultDropAction(Qt.MoveAction)

        self.field_list.setSizePolicy(QSizePolicy.Maximum,
                                      QSizePolicy.Expanding)
        # noinspection PyUnresolvedReferences
        self.field_list.itemSelectionChanged.connect(self.update_footer)

        # Footer
        self.footer_label = QLabel()
        self.footer_label.setWordWrap(True)

        # Parameters
        self.extra_parameters = [(GroupSelectParameter,
                                  GroupSelectParameterWidget)]

        self.parameters = []
        self.parameter_container = None

        # Adding to layout
        self.header_layout.addWidget(self.header_label)

        self.field_layout.addWidget(self.field_description)
        self.field_layout.addWidget(self.field_list)
        self.field_layout.setSizeConstraint(QLayout.SetMaximumSize)

        self.content_layout.addLayout(self.field_layout)
        self.content_layout.addLayout(self.parameter_layout)

        self.footer_layout.addWidget(self.footer_label)

        self.main_layout.addLayout(self.header_layout)
        self.main_layout.addLayout(self.content_layout)
        self.main_layout.addLayout(self.footer_layout)

        self.setLayout(self.main_layout)
Beispiel #47
0
__email__ = "*****@*****.**"
__revision__ = '8008783db0ca72c7a950460515728c12b5977d8d'

# Individual report component
analysis_question_component = {
    'key': 'analysis-question',
    'type': jinja2_component_type,
    'processor': jinja2_renderer,
    'extractor': analysis_question_extractor,
    'output_format': Jinja2ComponentsMetadata.OutputFormat.String,
    'output_path': 'analysis-result-output.html',
    'template': 'standard-template/'
    'jinja2/'
    'analysis-question.html',
    'extra_args': {
        'header': tr('Analysis question')
    }
}

general_report_component = {
    'key': 'general-report',
    'type': jinja2_component_type,
    'processor': jinja2_renderer,
    'extractor': general_report_extractor,
    'output_format': Jinja2ComponentsMetadata.OutputFormat.String,
    'output_path': 'general-report-output.html',
    'template': 'standard-template/'
    'jinja2/'
    'general-report.html',
    'extra_args': {
        'header':
Beispiel #48
0
# coding=utf-8
"""Caveats and advisories that will be shown in reports.

Some of these may be shown only in certain contexts.
"""

from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'fdf1afffab4271e5fbb873566278d5b4a7ff5722'

caveat_simulation = tr(
    'The extent and severity of the mapped scenario or hazard zones '
    'may not be consistent with future events.')
caveat_local_conditions = tr(
    'The impacts on roads, people, buildings and other exposure '
    'elements may differ from the analysis results due to local '
    'conditions such as terrain and infrastructure type.')
caveat_incomplete_data = tr(
    'The impacts on roads, people, buildings and other exposure '
    'elements may be underestimated if the exposure data are incomplete.')
caveat_analysis_extent = tr(
    'The analysis extent is limited to the extent of the aggregation '
    'layer or analysis extent. Hazard and exposure data outside the analysis '
    'extent are not included in the impact layer, impact map or impact '
    'reports.')
caveat_analysis_ommission = tr(
    'Structures overlapping the analysis extent may be assigned a hazard '
    'status lower than that to which they are exposed outside the analysis '
Beispiel #49
0
        count_ratio_mapping[count_fields[index]
                            ['key']] = ratio_fields[index]['key']

all_field_groups = [
    age_ratio_group, age_count_group, age_displaced_count_group,
    gender_ratio_group, gender_count_group, gender_displaced_count_group,
    disability_vulnerability_ratio_group, disability_vulnerability_count_group,
    disability_vulnerability_displaced_count_group,
    gender_vulnerability_ratio_group, gender_vulnerability_count_group,
    gender_vulnerability_displaced_count_group, age_vulnerability_ratio_group,
    age_vulnerability_count_group, age_vulnerability_displaced_count_group
]

# Update notes for each group
age_group_notes = [
    tr('Infant: {note}').format(note=concepts['infant']['description']),
    tr('Child: {note}').format(note=concepts['child']['description']),
    tr('Youth: {note}').format(note=concepts['youth']['description']),
    tr('Adult: {note}').format(note=concepts['adult']['description']),
    tr('Elderly: {note}').format(note=concepts['elderly']['description'])
]

gender_group_notes = [
    tr('Male: {note}').format(note=concepts['male']['description']),
    tr('Female: {note}').format(note=concepts['female']['description']),
]

age_vulnerability_group_notes = [
    tr('Under 5: {note}').format(note=concepts['under_5']['description']),
    tr('Over 60: {note}').format(note=concepts['over_60']['description']),
]
Beispiel #50
0
def content():
    """Helper method that returns just the content.

    This method was added so that the text could be reused in the
    dock_help module.

    .. versionadded:: 4.1.0

    :returns: A message object without brand element.
    :rtype: safe.messaging.message.Message
    """
    message = m.Message()
    message.add(
        m.Paragraph(
            tr('This section of the help documentation is intended for advanced '
               'users who want to modify the internals of InaSAFE. It assumes that '
               'you have basic coding skills. All examples are in python unless '
               'otherwise stated.')))
    message.add(m.Heading(tr('Defining a new hazard type'),
                          **SUBSECTION_STYLE))
    message.add(m.Heading(tr('Background'), **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(
        tr('In the previous versions of InaSAFE, we spent a lot of effort '
           'building one impact function per hazard/exposure combination (and '
           'sometimes multiple impact functions per combination). In our new '
           'architecture, we try to deal with everything in the same way - by '
           'following a standardized process of converting the hazard dataset '
           'into a classified polygon layer and then calculating the impacted '
           'and affected areas using a standard work-flow. A simplified version '
           'of this work-flow is described in illustration 1.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Because of this change, you will no longer see an impact function '
           'selector in the dock widget and there are no longer any \'impact '
           'function options\' as we had in previous versions of InaSAFE. In '
           'the new system, almost all configuration is managed through '
           'metadata (created using the keywords wizard).'))

    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Also, in all versions prior to Version 4.0, we made heavy use of '
           'interpolation in order to determine whether buildings or other '
           'exposure layers are impacted. While this is a commonly used '
           'technique in GIS, it often leads to non - intuitive looking '
           'reports. Under our new architecture, we always use geometric '
           'overlay operations to make a determination whether an exposure '
           'feature is affected or not. The implication of this is that we '
           'produce intuitive and easily verifiable impact layers. You can '
           'see an example in Illustration 2.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Stepping away from the two previously mentioned paradigms allows '
           'us to simply add new hazard types to the metadata driven impact '
           'function by adding new metadata types to the InaSAFE sources. '
           'In the next chapter we show you how this was achieved and how '
           'it can be repeated for further hazards using the example of '
           'tropical cyclones.'))
    message.add(paragraph)
    message.add(m.Heading(tr('Adding a new hazard'), **BLUE_CHAPTER_STYLE))
    link = m.Link('https://github.com/inasafe/inasafe/pull/3539/files',
                  tr('Pull Request #3539'))
    paragraph = m.Paragraph(
        tr('The whole work needed can be looked at in '), link,
        tr('. Please bear in mind that the paths of the files are now '
           'safe/definitions/xxx.py and not safe/definitionsv4/xxx.py since '
           'v4 is the default codebase. In the next sections we will show '
           'each file that needs to be extended in order to add a new hazard '
           'type.'))
    message.add(paragraph)

    # Setting up units

    message.add(
        m.Heading(tr('safe/definitions/units.py'), **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(
        tr('If you are adding an hazard that uses units that are not yet known '
           'to InaSAFE, you need to define them in units.py'))
    message.add(paragraph)
    paragraph = m.PreformattedText(
        _get_definition_from_module(units, 'unit_kilometres_per_hour'))
    message.add(paragraph)

    # Setting up style

    message.add(m.Heading('safe/definitions/colors.py', **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(
        'If you are adding an hazard that has more classes than any other '
        'hazards you’ll need to add additional colors for the additional '
        'classes in colors.py. You might also define other colors if you '
        'don\'t want to use the standard colors. For the sake of homogeneous '
        'map reports, this addition should not be taken lightly.')
    message.add(paragraph)
    # Don't translate this
    paragraph = m.PreformattedText('very_dark_red = Qcolor(\'#710017\')')
    message.add(paragraph)

    # Setting up hazard classification

    message.add(
        m.Heading('safe/definitions/hazard_classifications.py',
                  **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(
        tr('Add the classifications you want to make available for your new '
           'hazard type. You can add as many classes as you want in the '
           'classes list.'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Also, a classification can support multiple units so you don\'t '
           'have to define different classifications just to have the same '
           'classification in two or more different units. These are defined '
           'in the multiple_units attribute of the classification.'))
    message.add(paragraph)

    paragraph = m.PreformattedText(
        _get_definition_from_module(hazard_classifications,
                                    'cyclone_au_bom_hazard_classes'))
    message.add(paragraph)

    # Setting up wizard questions

    message.add(
        m.Heading('safe/gui/tools/wizard/wizard_strings.py',
                  **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(tr('Define the questions for the wizard:'))
    message.add(paragraph)
    # don not translate
    message.add(
        m.PreformattedText(
            'cyclone_kilometres_per_hour_question = tr(\'wind speed in km/h\')'
        ))
    message.add(
        m.PreformattedText(
            'cyclone_miles_per_hour_question = tr(\'wind speed in mph\')'))
    message.add(
        m.PreformattedText(
            'cyclone_knots_question = tr(\'wind speed in kn\')'))

    # Setting up

    message.add(m.Heading('safe/definitions/hazard.py', **BLUE_CHAPTER_STYLE))
    paragraph = m.Paragraph(
        tr('Finally define new hazard and add it to the hazard_all list:'))
    message.add(paragraph)
    paragraph = m.PreformattedText(
        _get_definition_from_module(hazard, 'hazard_cyclone'))
    message.add(paragraph)
    paragraph = m.Paragraph(
        tr('Finally define new hazard and add it to the hazard_all list:'))
    message.add(paragraph)
    paragraph = m.PreformattedText(
        _get_definition_from_module(hazard, 'hazard_all'))
    message.add(paragraph)
    return message
Beispiel #51
0
def clip(layer_to_clip, mask_layer, callback=None):
    """Clip a vector layer with another.

    Issue https://github.com/inasafe/inasafe/issues/3186

    Note : This algorithm is copied from :
    https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/
    qgis/Clip.py

    :param layer_to_clip: The vector layer to clip.
    :type layer_to_clip: QgsVectorLayer

    :param mask_layer: The vector layer to use for clipping.
    :type mask_layer: QgsVectorLayer

    :param callback: A function to all to indicate progress. The function
        should accept params 'current' (int), 'maximum' (int) and 'step' (str).
        Defaults to None.
    :type callback: function

    :return: The clip vector layer.
    :rtype: QgsVectorLayer

    .. versionadded:: 4.0
    """
    output_layer_name = clip_steps['output_layer_name']
    output_layer_name = output_layer_name % (
        layer_to_clip.keywords['layer_purpose'])
    processing_step = clip_steps['step_name']

    writer = create_memory_layer(output_layer_name,
                                 layer_to_clip.geometryType(),
                                 layer_to_clip.crs(), layer_to_clip.fields())
    writer.startEditing()

    # Begin copy/paste from Processing plugin.
    # Please follow their code as their code is optimized.
    # The code below is not following our coding standards because we want to
    # be able to track any diffs from QGIS easily.

    # first build up a list of clip geometries
    clip_geometries = []
    request = QgsFeatureRequest().setSubsetOfAttributes([])
    for mask_feature in mask_layer.getFeatures(request):
        clip_geometries.append(QgsGeometry(mask_feature.geometry()))

    # are we clipping against a single feature? if so,
    # we can show finer progress reports
    if len(clip_geometries) > 1:
        # noinspection PyTypeChecker,PyCallByClass,PyArgumentList
        combined_clip_geom = QgsGeometry.unaryUnion(clip_geometries)
        single_clip_feature = False
    else:
        combined_clip_geom = clip_geometries[0]
        single_clip_feature = True

    # use prepared geometries for faster intersection tests
    # noinspection PyArgumentList
    engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry())
    engine.prepareGeometry()

    tested_feature_ids = set()

    for i, clip_geom in enumerate(clip_geometries):
        request = QgsFeatureRequest().setFilterRect(clip_geom.boundingBox())
        input_features = [f for f in layer_to_clip.getFeatures(request)]

        if not input_features:
            continue

        if single_clip_feature:
            total = 100.0 / len(input_features)
        else:
            total = 0

        for current, in_feat in enumerate(input_features):
            if not in_feat.geometry():
                continue

            if in_feat.id() in tested_feature_ids:
                # don't retest a feature we have already checked
                continue

            tested_feature_ids.add(in_feat.id())

            if not engine.intersects(in_feat.geometry().geometry()):
                continue

            if not engine.contains(in_feat.geometry().geometry()):
                cur_geom = in_feat.geometry()
                new_geom = combined_clip_geom.intersection(cur_geom)
                if new_geom.wkbType() == QgsWKBTypes.Unknown \
                        or QgsWKBTypes.flatType(
                            new_geom.geometry().wkbType()) == \
                                QgsWKBTypes.GeometryCollection:
                    int_com = in_feat.geometry().combine(new_geom)
                    int_sym = in_feat.geometry().symDifference(new_geom)
                    if not int_com or not int_sym:
                        # LOGGER.debug(
                        #     tr('GEOS geoprocessing error: One or more input '
                        #        'features have invalid geometry.'))
                        pass
                    else:
                        new_geom = int_com.difference(int_sym)
                        if new_geom.isGeosEmpty()\
                                or not new_geom.isGeosValid():
                            # LOGGER.debug(
                            #     tr('GEOS geoprocessing error: One or more '
                            #        'input features have invalid geometry.'))
                            pass
            else:
                # clip geometry totally contains feature geometry,
                # so no need to perform intersection
                new_geom = in_feat.geometry()

            try:
                out_feat = QgsFeature()
                out_feat.setGeometry(new_geom)
                out_feat.setAttributes(in_feat.attributes())
                if new_geom.type() == layer_to_clip.geometryType():
                    writer.addFeature(out_feat)
            except:
                LOGGER.debug(
                    tr('Feature geometry error: One or more output features '
                       'ignored due to invalid geometry.'))
                continue

            # TODO implement callback
            if single_clip_feature:
                # progress.setPercentage(int(current * total))
                pass

        if not single_clip_feature:
            # coarse progress report for multiple clip geometries
            # progress.setPercentage(100.0 * i / len(clip_geoms))
            pass

    # End copy/paste from Processing plugin.
    writer.commitChanges()

    writer.keywords = layer_to_clip.keywords.copy()
    writer.keywords['title'] = output_layer_name
    check_layer(writer)
    return writer
Beispiel #52
0
# coding=utf-8
"""This module contains constants that are used in definitions."""

from PyQt4.QtCore import QVariant
from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = '8ecf2f4ed59dc0518a3013c08b8f4f865bbfe1d7'

inasafe_keyword_version_key = 'keyword_version'
no_field = tr('No Field')
no_data_value = 200
zero_default_value = 0.0
big_number = 9999

# Whole Number in QVariant / Integer as in math
qvariant_whole_numbers = [
    QVariant.Int,
    QVariant.UInt,
    QVariant.LongLong,
    QVariant.ULongLong
]

# Number in QVariant
qvariant_numbers = qvariant_whole_numbers + [
    QVariant.Double,
]

qvariant_all = qvariant_numbers + [QVariant.String]
from safe.utilities.i18n import tr

__copyright__ = "Copyright 2017, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'fdf1afffab4271e5fbb873566278d5b4a7ff5722'

EXTRA_CALORIES_NEEDED_PER_DAY = 500  # in KKal / day
DAY_IN_A_WEEK = 7  # in day / week
KG_RICE_PER_CALORIES = 0.1 / 129  # in KKal (100 gram gives 129 KKal calories)

post_processor_additional_rice = {
    'key':
    'post_processor_additional_rice',
    'name':
    tr('Additional Weekly Rice kg for Pregnant and Lactating Women Post '
       'Processor'),
    'description':
    tr('A post processor to calculate additional rice for pregnant and '
       'lactating women who are displaced. '
       '"Displaced" is defined as: {displaced_concept}').format(
           displaced_concept=concepts['displaced_people']['description']),
    'input': {
        'pregnant_displaced': [{
            'value': pregnant_displaced_count_field,
            'type': field_input_type,
        }],
        'lactating_displaced': [{
            'value': lactating_displaced_count_field,
            'type': field_input_type,
        }],
        'additional_rice_ratio': {
Beispiel #54
0
def minimum_needs_extractor(impact_report, component_metadata):
    """Extracting minimum needs of the impact layer.

    :param impact_report: the impact report that acts as a proxy to fetch
        all the data that extractor needed
    :type impact_report: safe.report.impact_report.ImpactReport

    :param component_metadata: the component metadata. Used to obtain
        information about the component we want to render
    :type component_metadata: safe.report.report_metadata.
        ReportComponentsMetadata

    :return: context for rendering phase
    :rtype: dict

    .. versionadded:: 4.0
    """
    context = {}
    extra_args = component_metadata.extra_args
    analysis_layer = impact_report.analysis
    analysis_keywords = analysis_layer.keywords['inasafe_fields']
    use_rounding = impact_report.impact_function.use_rounding

    header = resolve_from_dictionary(extra_args, 'header')
    context['header'] = header

    # check if displaced is not zero
    try:
        displaced_field_name = analysis_keywords[displaced_field['key']]
        total_displaced = value_from_field_name(displaced_field_name,
                                                analysis_layer)
        if total_displaced == 0:
            zero_displaced_message = resolve_from_dictionary(
                extra_args, 'zero_displaced_message')
            context['zero_displaced'] = {
                'status': True,
                'message': zero_displaced_message
            }
            return context
    except KeyError:
        # in case no displaced field
        pass

    # minimum needs calculation only affect population type exposure
    # check if analysis keyword have minimum_needs keywords
    have_minimum_needs_field = False
    for field_key in analysis_keywords:
        if field_key.startswith(minimum_needs_namespace):
            have_minimum_needs_field = True
            break

    if not have_minimum_needs_field:
        return context

    frequencies = {}
    # map each needs to its frequency groups
    for field in (minimum_needs_fields + additional_minimum_needs):
        need_parameter = field.get('need_parameter')
        if isinstance(need_parameter, ResourceParameter):
            frequency = need_parameter.frequency
        else:
            frequency = field.get('frequency')

        if frequency:
            if frequency not in frequencies:
                frequencies[frequency] = [field]
            else:
                frequencies[frequency].append(field)

    needs = []
    analysis_feature = next(analysis_layer.getFeatures())
    header_frequency_format = resolve_from_dictionary(
        extra_args, 'header_frequency_format')
    total_header = resolve_from_dictionary(extra_args, 'total_header')
    need_header_format = resolve_from_dictionary(extra_args,
                                                 'need_header_format')
    # group the needs by frequency
    for key, frequency in list(frequencies.items()):
        group = {
            'header': header_frequency_format.format(frequency=tr(key)),
            'total_header': total_header,
            'needs': []
        }
        for field in frequency:
            # check value exists in the field
            field_idx = analysis_layer.fields().lookupField(
                field['field_name'])
            if field_idx == -1:
                # skip if field doesn't exists
                continue
            value = format_number(analysis_feature[field_idx],
                                  use_rounding=use_rounding,
                                  is_population=True)

            unit_abbreviation = ''
            if field.get('need_parameter'):
                need_parameter = field['need_parameter']
                """:type: ResourceParameter"""
                name = tr(need_parameter.name)
                unit_abbreviation = need_parameter.unit.abbreviation

            else:
                if field.get('header_name'):
                    name = field.get('header_name')
                else:
                    name = field.get('name')

                need_unit = field.get('unit')
                if need_unit:
                    unit_abbreviation = need_unit.get('abbreviation')

            if unit_abbreviation:
                header = need_header_format.format(
                    name=name, unit_abbreviation=unit_abbreviation)
            else:
                header = name

            item = {'header': header, 'value': value}
            group['needs'].append(item)
        needs.append(group)

    context['component_key'] = component_metadata.key
    context['needs'] = needs

    return context
Beispiel #55
0
# coding=utf-8
"""
Definitions relating to layer modes (continuous or classified data).
"""

from safe.utilities.i18n import tr

__copyright__ = "Copyright 2016, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'b68f9f9b473d275ea4d63c7b831184f2c08e0e6e'


layer_mode_continuous = {
    'key': 'continuous',
    'name': tr('Continuous'),
    'description': tr(
        '<b>Continuous</b> data can be used in raster hazard or exposure data '
        'where the values in the data are either integers or decimal values '
        'representing a continuously varying phenomenon. For example flood '
        'depth is a continuous value from 0 to the maximum reported depth '
        'during a flood. <p>Raster exposure data such as population data are '
        'also continuous. In this example the cell values represent the '
        'number of people in cell.</p>'
        '<p>Raster data is considered to be continuous by default and you '
        'should explicitly indicate that it is classified if each cell in the '
        'raster represents a discrete class (e.g. low depth = 1, medium depth '
        '= 2, high depth = 3).</p>'
    ),
    'citations': [
        {
    def build_widget(self, form_layout, name, key_value):
        """Create a new form element dynamically based from theValue type.
        The element will be inserted to theFormLayout.

        :param form_layout: Mandatory a layout instance
        :type form_layout: QFormLayout

        :param name: Mandatory string referencing the key in the function
         configurable parameters dictionary.
        :type name: str

        :param key_value: Mandatory representing the value referenced by the
         key.
        :type key_value: object

        :returns: a function that return the value of widget

        :raises: None
        """

        # create label
        if isinstance(name, str):
            label = QLabel()
            label.setObjectName(_fromUtf8(name + "Label"))
            label_text = name.replace('_', ' ').capitalize()
            label.setText(tr(label_text))
            label.setToolTip(str(type(key_value)))
        else:
            label = name

        # create widget based on the type of key_value variable
        # if widget is a QLineEdit, value needs to be set
        # if widget is NOT a QLineEdit, property_name needs to be set
        value = None
        property_name = None

        # can be used for widgets that have their own text like QCheckBox
        hide_label = False

        if isinstance(key_value, list):

            def function(values_string):
                """
                :param values_string: This contains the list of values the user
                    added to the line edit for the parameter.
                :type values_string: basestring

                :returns: list of value types
                :rtype: list
                """
                value_type = type(key_value[0])
                return [value_type(y) for y in str(values_string).split(',')]

            widget = QLineEdit()
            value = ', '.join([str(x) for x in key_value])
            # NOTE: we assume that all element in list have same type
        elif isinstance(key_value, dict):

            def function(key_values_string):
                """
                :param key_values_string: This contains the dictionary that
                    the used defined on the line edit for this parameter.
                :type key_values_string: basestring

                :returns: a safe evaluation of the dict
                :rtype: dict
                """
                return ast.literal_eval(str(key_values_string))

            widget = QLineEdit()
            value = str(key_value)
        elif isinstance(key_value, bool):
            function = bool
            widget = QCheckBox()
            widget.setChecked(key_value)
            widget.setText(label.text())
            property_name = 'checked'
            hide_label = True
        else:
            function = type(key_value)
            widget = QLineEdit()
            value = str(key_value)
        if hide_label:
            form_layout.addRow(widget)
        else:
            form_layout.addRow(label, widget)

        if isinstance(widget, QLineEdit):
            widget.setText(value)
            property_name = 'text'

        return self.bind(widget, property_name, function)
Beispiel #57
0
from safe.definitions.fields import (child_bearing_age_displaced_count_field,
                                     pregnant_displaced_count_field,
                                     lactating_displaced_count_field)

from safe.utilities.i18n import tr

__copyright__ = "Copyright 2017, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = 'b68f9f9b473d275ea4d63c7b831184f2c08e0e6e'

gender_vulnerability_ratio_group = {
    'key':
    'gender_vulnerability_ratio_group',
    'name':
    tr('Gender Vulnerability Ratio'),
    'description':
    tr('Demographic breakdown to use for displaced population based on '
       'gender. Vulnerability ratio groupings are used when there '
       'is a vector aggregation layer that contains detailed demographic '
       'information (as ratios) about the population living in each '
       'administrative or census area. These ratios are then applied to the '
       'count of displaced population per aggregation area to provide a more '
       'detailed break down of the number of people displaced in each '
       'gender profile. Vulnerable segments of the population '
       'can include criteria like the number of infants, the number of '
       'elderly, the number of disabled people, and so on.'),
    'fields': [
        child_bearing_age_ratio_field, pregnant_ratio_field,
        lactating_ratio_field
    ],
Beispiel #58
0
    def as_dict():
        """Return metadata as a dictionary.

        This is a static method. You can use it to get the metadata in
        dictionary format for an impact function.

        :returns: A dictionary representing all the metadata for the
            concrete impact function.
        :rtype: dict
        """
        dict_meta = {
            'id':
            'FloodRasterBuildingFunction',
            'name':
            tr('Raster flood on buildings'),
            'impact':
            tr('Be flooded'),
            'title':
            tr('Be flooded'),
            'function_type':
            'old-style',
            # should be a list, but we can do it later.
            'author':
            'Ole Nielsen and Kristy van Putten',
            'date_implemented':
            'N/A',
            'overview':
            tr('To assess the impacts of (flood or tsunami) inundation '
               'on building footprints originating from OpenStreetMap '
               '(OSM) with hazard in raster format.'),
            'detailed_description':
            tr('The inundation status is calculated for each building '
               '(using the centroid if it is a polygon) based on the '
               'flood threshold. The threshold can be configured in '
               'impact function options.'),
            'hazard_input':
            tr('A hazard raster layer where each cell represents flood '
               'depth (in meters).'),
            'exposure_input':
            tr('Vector polygon or point layer extracted from OSM where '
               'each feature represents the footprint of a building.'),
            'output':
            tr('Vector layer contains building is estimated to be '
               'flooded and the breakdown of the building by type.'),
            'actions':
            tr('Provide details about where critical infrastructure '
               'might be flooded.'),
            'limitations': [
                tr('This function only flags buildings as impacted or not '
                   'either based on a fixed threshold')
            ],
            'citations': [],
            'layer_requirements': {
                'hazard': {
                    'layer_mode':
                    layer_mode_continuous,
                    'layer_geometries': [layer_geometry_raster],
                    'hazard_categories': [
                        hazard_category_single_event,
                        hazard_category_multiple_event
                    ],
                    'hazard_types': [hazard_flood],
                    'continuous_hazard_units': [unit_feet, unit_metres],
                    'vector_hazard_classifications': [],
                    'raster_hazard_classifications': [],
                    'additional_keywords': []
                },
                'exposure': {
                    'layer_mode':
                    layer_mode_classified,
                    'layer_geometries':
                    [layer_geometry_point, layer_geometry_polygon],
                    'exposure_types': [exposure_structure],
                    'exposure_units': [],
                    'exposure_class_fields': [structure_class_field],
                    'additional_keywords': []
                }
            },
            'parameters':
            OrderedDict([('threshold', threshold()),
                         ('postprocessors',
                          OrderedDict([('BuildingType',
                                        building_type_postprocessor())]))])
        }
        return dict_meta
Beispiel #59
0
from safe.definitions.fields import (female_ratio_field, male_ratio_field,
                                     female_count_field, male_count_field,
                                     male_displaced_count_field,
                                     female_displaced_count_field)
from safe.utilities.i18n import tr

__copyright__ = "Copyright 2017, The InaSAFE Project"
__license__ = "GPL version 3"
__email__ = "*****@*****.**"
__revision__ = '2f075ced569597e81df73adc9a7e1439daa99814'

gender_ratio_group = {
    'key':
    'gender_ratio_group',
    'name':
    tr('Gender Ratio'),
    'description':
    tr('Demographic breakdown to use for displaced population based on '
       'gender. Gender ratio groupings are used when there is a vector '
       'aggregation layer that contains detailed demographic information ( '
       'as ratios) about the population living in each administrative or '
       'census area. These ratios are then applied to the count of displaced '
       'population per aggregation area to provide a more detailed break '
       'down of the number of people displaced in each gender profile. '
       'Gender specific info can include criteria like the number of '
       'females, the number of females of child bearing age, and so on.'),
    'fields': [
        male_ratio_field,
        female_ratio_field,
    ],
    # Exclusive = False: able to put same layer's field to some fields
Beispiel #60
0
def intersection(source, mask, callback=None):
    """Intersect two layers.

    Issue https://github.com/inasafe/inasafe/issues/3186

    Note : This algorithm is copied from :
    https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/
    qgis/Intersection.py

    :param source: The vector layer to clip.
    :type source: QgsVectorLayer

    :param mask: The vector layer to use for clipping.
    :type mask: QgsVectorLayer

    :param callback: A function to all to indicate progress. The function
        should accept params 'current' (int), 'maximum' (int) and 'step' (str).
        Defaults to None.
    :type callback: function

    :return: The clip vector layer.
    :rtype: QgsVectorLayer

    .. versionadded:: 4.0
    """
    output_layer_name = intersection_steps['output_layer_name']
    output_layer_name = output_layer_name % (source.keywords['layer_purpose'])
    processing_step = intersection_steps['step_name']

    fields = source.fields()
    fields.extend(mask.fields())

    writer = create_memory_layer(output_layer_name, source.geometryType(),
                                 source.crs(), fields)

    writer.startEditing()

    # Begin copy/paste from Processing plugin.
    # Please follow their code as their code is optimized.
    # The code below is not following our coding standards because we want to
    # be able to track any diffs from QGIS easily.

    out_feature = QgsFeature()
    index = create_spatial_index(mask)

    # Todo callback
    # total = 100.0 / len(selectionA)

    for current, in_feature in enumerate(source.getFeatures()):
        # progress.setPercentage(int(current * total))
        geom = in_feature.geometry()
        attributes = in_feature.attributes()
        intersects = index.intersects(geom.boundingBox())
        for i in intersects:
            request = QgsFeatureRequest().setFilterFid(i)
            feature_mask = next(mask.getFeatures(request))
            tmp_geom = feature_mask.geometry()
            if geom.intersects(tmp_geom):
                mask_attributes = feature_mask.attributes()
                int_geom = QgsGeometry(geom.intersection(tmp_geom))
                if int_geom.wkbType() == QgsWKBTypes.Unknown\
                        or QgsWKBTypes.flatType(
                        int_geom.geometry().wkbType()) ==\
                                QgsWKBTypes.GeometryCollection:
                    int_com = geom.combine(tmp_geom)
                    int_geom = QgsGeometry()
                    if int_com:
                        int_sym = geom.symDifference(tmp_geom)
                        int_geom = QgsGeometry(int_com.difference(int_sym))
                if int_geom.isGeosEmpty() or not int_geom.isGeosValid():
                    # LOGGER.debug(
                    #     tr('GEOS geoprocessing error: One or more input '
                    #        'features have invalid geometry.'))
                    pass
                try:
                    geom_types = wkb_type_groups[wkb_type_groups[
                        int_geom.wkbType()]]
                    if int_geom.wkbType() in geom_types:
                        if int_geom.type() == source.geometryType():
                            # We got some features which have not the same
                            # kind of geometry. We want to skip them.
                            out_feature.setGeometry(int_geom)
                            attrs = []
                            attrs.extend(attributes)
                            attrs.extend(mask_attributes)
                            out_feature.setAttributes(attrs)
                            writer.addFeature(out_feature)
                except:
                    LOGGER.debug(
                        tr('Feature geometry error: One or more output '
                           'features ignored due to invalid geometry.'))
                    continue

    # End copy/paste from Processing plugin.
    writer.commitChanges()

    writer.keywords = dict(source.keywords)
    writer.keywords['title'] = output_layer_name
    writer.keywords['layer_purpose'] = layer_purpose_exposure_summary['key']
    writer.keywords['inasafe_fields'] = dict(source.keywords['inasafe_fields'])
    writer.keywords['inasafe_fields'].update(mask.keywords['inasafe_fields'])
    writer.keywords['hazard_keywords'] = dict(mask.keywords['hazard_keywords'])
    writer.keywords['exposure_keywords'] = dict(source.keywords)
    writer.keywords['aggregation_keywords'] = dict(
        mask.keywords['aggregation_keywords'])

    check_layer(writer)
    return writer