def run(self):
        """Risk plugin for classified polygon hazard on building/structure.

        Counts number of building exposed to each hazard zones.

        :returns: Impact vector layer building exposed to each hazard zones.
            Table with number of buildings affected
        :rtype: Vector
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step',
            'Impact function is calculating the impact.')

        # Value from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Retrieve the classification that is used by the hazard layer.
        vector_hazard_classification = self.hazard.keyword(
            'vector_hazard_classification')
        # Get the dictionary that contains the definition of the classification
        vector_hazard_classification = definition(vector_hazard_classification)
        # Get the list classes in the classification
        vector_hazard_classes = vector_hazard_classification['classes']
        # Initialize OrderedDict of affected buildings
        self.affected_buildings = OrderedDict()
        # Iterate over vector hazard classes
        for vector_hazard_class in vector_hazard_classes:
            # Check if the key of class exist in hazard_class_mapping
            if vector_hazard_class['key'] in self.hazard_class_mapping.keys():
                # Replace the key with the name as we need to show the human
                # friendly name in the report.
                self.hazard_class_mapping[vector_hazard_class['name']] = \
                    self.hazard_class_mapping.pop(vector_hazard_class['key'])
                # Adding the class name as a key in affected_building
                self.affected_buildings[vector_hazard_class['name']] = {}

        hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex(
            self.hazard_class_attribute)

        # Check if hazard_zone_attribute exists in hazard_layer
        if hazard_zone_attribute_index < 0:
            message = (
                'Hazard data %s does not contain expected attribute %s ' %
                (self.hazard.layer.name(), self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Hazard zone categories from hazard layer
        unique_values = self.hazard.layer.uniqueValues(
            hazard_zone_attribute_index)
        # Values might be integer or float, we should have unicode. #2626
        self.hazard_zones = [get_unicode(val) for val in unique_values]

        self.buildings = {}

        wgs84_extent = QgsRectangle(
            self.requested_extent[0], self.requested_extent[1],
            self.requested_extent[2], self.requested_extent[3])

        # Run interpolation function for polygon2polygon
        interpolated_layer = interpolate_polygon_polygon(
            self.hazard.layer, self.exposure.layer, wgs84_extent)

        new_field = QgsField(self.target_field, QVariant.String)
        interpolated_layer.dataProvider().addAttributes([new_field])
        interpolated_layer.updateFields()

        attribute_names = [
            field.name() for field in interpolated_layer.pendingFields()]
        target_field_index = interpolated_layer.fieldNameIndex(
            self.target_field)
        changed_values = {}

        if interpolated_layer.featureCount() < 1:
            raise ZeroImpactException()

        # Extract relevant interpolated data
        for feature in interpolated_layer.getFeatures():
            # Get the hazard value based on the value mapping in keyword
            hazard_value = get_key_for_value(
                    feature[self.hazard_class_attribute],
                    self.hazard_class_mapping)
            if not hazard_value:
                hazard_value = self._not_affected_value
            changed_values[feature.id()] = {target_field_index: hazard_value}

            if (self.exposure_class_attribute and
                        self.exposure_class_attribute in attribute_names):
                usage = feature[self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, feature)

            if usage is None:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.hazard_class_mapping.keys():
                    self.affected_buildings[category][usage] = OrderedDict(
                        [(tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            if hazard_value in self.hazard_class_mapping.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        interpolated_layer.dataProvider().changeAttributeValues(changed_values)

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        categories = self.affected_buildings.keys()
        categories.append(self._not_affected_value)
        colours = color_ramp(len(categories))
        style_classes = []

        i = 0
        for hazard_zone in self.affected_buildings.keys():
            style_class = dict()
            style_class['label'] = tr(hazard_zone)
            style_class['transparency'] = 0
            style_class['value'] = hazard_zone
            style_class['size'] = 1
            style_class['colour'] = colours[i]
            style_classes.append(style_class)
            i += 1

        # Override style info with new classes and name
        style_info = dict(
            target_field=self.target_field,
            style_classes=style_classes,
            style_type='categorizedSymbol'
        )

        # For printing map purpose
        map_title = tr('Buildings affected by each hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr(
            'Thousand separator is represented by %s' %
            get_thousand_separator())

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Vector(
            data=interpolated_layer,
            name=tr('Buildings affected by each hazard zone'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
    def run(self):
        """Risk plugin for volcano hazard on building/structure.

        Counts number of building exposed to each volcano hazard zones.

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step',
            'Impact function is calculating the impact.')

        # Get parameters from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.name_attribute = self.hazard.keyword('volcano_name_field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Input checks
        if not self.hazard.layer.is_polygon_data:
            message = (
                'Input hazard must be a polygon. I got %s with '
                'layer type %s' %
                (self.hazard.name, self.hazard.layer.get_geometry_name()))
            raise Exception(message)

        # Check if hazard_zone_attribute exists in hazard_layer
        if (self.hazard_class_attribute not in
                self.hazard.layer.get_attribute_names()):
            message = (
                'Hazard data %s did not contain expected attribute %s ' %
                (self.hazard.name, self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Get names of volcanoes considered
        if self.name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = set()
            for row in self.hazard.layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[self.name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)
        else:
            self.volcano_names = tr('Not specified in data')

        # Retrieve the classification that is used by the hazard layer.
        vector_hazard_classification = self.hazard.keyword(
            'vector_hazard_classification')
        # Get the dictionary that contains the definition of the classification
        vector_hazard_classification = definition(vector_hazard_classification)
        # Get the list classes in the classification
        vector_hazard_classes = vector_hazard_classification['classes']
        # Initialize OrderedDict of affected buildings
        self.affected_buildings = OrderedDict()
        # Iterate over vector hazard classes
        for vector_hazard_class in vector_hazard_classes:
            # Check if the key of class exist in hazard_class_mapping
            if vector_hazard_class['key'] in self.hazard_class_mapping.keys():
                # Replace the key with the name as we need to show the human
                # friendly name in the report.
                self.hazard_class_mapping[vector_hazard_class['name']] = \
                    self.hazard_class_mapping.pop(vector_hazard_class['key'])
                # Adding the class name as a key in affected_building
                self.affected_buildings[vector_hazard_class['name']] = {}

        # Run interpolation function for polygon2raster
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer, self.exposure.layer)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        self.buildings = {}

        for i in range(len(features)):
            # Get the hazard value based on the value mapping in keyword
            hazard_value = get_key_for_value(
                    features[i][self.hazard_class_attribute],
                    self.hazard_class_mapping)
            if not hazard_value:
                hazard_value = self._not_affected_value
            features[i][self.target_field] = get_string(hazard_value)

            if (self.exposure_class_attribute and
                    self.exposure_class_attribute in attribute_names):
                usage = features[i][self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, features[i])

            if usage in [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][
                        usage] = OrderedDict([
                            (tr('Buildings Affected'), 0)])

            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        colours = colours[::-1]  # flip

        colours = colours[:len(self.affected_buildings.keys())]

        style_classes = []

        i = 0
        for category_name in self.affected_buildings.keys():
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(self.affected_buildings.keys()):
                i = len(self.affected_buildings.keys()) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic hazard zone'),
            keywords=impact_layer_keywords,
            style_info=style_info
        )

        self._impact = impact_layer
        return impact_layer
Example #3
0
    def run(self):
        """Tsunami raster impact to buildings (e.g. from Open Street Map)."""
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step',
            'Impact function is calculating the impact.')

        # Thresholds for tsunami hazard zone breakdown.
        low_max = self.parameters['low_threshold'].value
        medium_max = self.parameters['medium_threshold'].value
        high_max = self.parameters['high_threshold'].value

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=self.target_field)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)

        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (self.hazard_classes[0], {}),
            (self.hazard_classes[1], {}),
            (self.hazard_classes[2], {}),
            (self.hazard_classes[3], {}),
            (self.hazard_classes[4], {})
        ])
        categories = self.affected_buildings.keys()
        for i in range(total_features):
            # Get the interpolated depth
            water_depth = float(features[i][self.target_field])
            if water_depth <= 0:
                inundated_status = 0
            elif 0 < water_depth <= low_max:
                inundated_status = 1  # low
            elif low_max < water_depth <= medium_max:
                inundated_status = 2  # medium
            elif medium_max < water_depth <= high_max:
                inundated_status = 3  # high
            elif high_max < water_depth:
                inundated_status = 4  # very high
            # If not a number or a value beside real number.
            else:
                inundated_status = 0

            # Count affected buildings by usage type if available
            if (structure_class_field in attribute_names and
                    structure_class_field):
                usage = features[i].get(structure_class_field, None)
            else:
                usage = get_osm_building_usage(
                    attribute_names, features[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            # Add calculated impact to existing attributes
            features[i][self.target_field] = inundated_status
            category = categories[inundated_status]
            self.affected_buildings[category][usage][
                tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()
        # Generate simple impact report
        impact_table = impact_summary = self.html_report()

        # For printing map purpose
        map_title = tr('Inundated buildings')
        legend_title = tr('Inundated structure status')
        legend_units = tr('(low, medium, high, and very high)')

        style_classes = [
            dict(
                label=self.hazard_classes[0] + ': 0 m',
                value=0,
                colour='#00FF00',
                transparency=0,
                size=1
            ),
            dict(
                label=self.hazard_classes[1] + ': 0.1 - %.1f m' % low_max,
                value=1,
                colour='#FFFF00',
                transparency=0,
                size=1
            ),
            dict(
                label=self.hazard_classes[2] + ': %.1f - %.1f m' % (
                    low_max + 0.1, medium_max),
                value=2,
                colour='#FFB700',
                transparency=0,
                size=1
            ),
            dict(
                label=self.hazard_classes[3] + ': %.1f - %.1f m' % (
                    medium_max + 0.1, high_max),
                value=3,
                colour='#FF6F00',
                transparency=0,
                size=1
            ),

            dict(
                label=self.hazard_classes[4] + ' > %.1f m' % high_max,
                value=4,
                colour='#FF0000',
                transparency=0,
                size=1
            ),
        ]

        style_info = dict(
            target_field=self.target_field,
            style_classes=style_classes,
            style_type='categorizedSymbol')

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_title': legend_title,
            'legend_units': legend_units,
            'buildings_total': total_features,
            'buildings_affected': self.total_affected_buildings
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        vector_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords=impact_layer_keywords,
            style_info=style_info)
        # Create vector layer and return
        self._impact = vector_layer
        return vector_layer
Example #4
0
    def run(self, layers=None):
        """Earthquake impact to buildings (e.g. from OpenStreetMap).

        :param layers: All the input layers (Hazard Layer and Exposure Layer)
        """
        self.validate()
        self.prepare(layers)

        LOGGER.debug('Running earthquake building impact')

        # merely initialize
        building_value = 0
        contents_value = 0

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

        # Class Attribute and Label.

        class_1 = {'label': tr('Low'), 'class': 1}
        class_2 = {'label': tr('Medium'), 'class': 2}
        class_3 = {'label': tr('High'), 'class': 3}

        # Extract data
        hazard_layer = self.hazard  # Depth
        exposure_layer = self.exposure  # Building locations

        # Define attribute name for hazard levels.
        hazard_attribute = 'mmi'

        # Determine if exposure data have NEXIS attributes.
        attribute_names = exposure_layer.get_attribute_names()
        if (
                'FLOOR_AREA' in attribute_names and
                'BUILDING_C' in attribute_names and
                'CONTENTS_C' in attribute_names):
            self.is_nexis = True
        else:
            self.is_nexis = False

        # Interpolate hazard level to building locations.
        interpolate_result = assign_hazard_values_to_exposure_data(
            hazard_layer,
            exposure_layer,
            attribute_name=hazard_attribute
        )

        # Extract relevant exposure data
        # attribute_names = interpolate_result.get_attribute_names()
        attributes = interpolate_result.get_data()

        interpolate_size = len(interpolate_result)

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (tr('High'), {}),
            (tr('Medium'), {}),
            (tr('Low'), {})
        ])
        for i in range(interpolate_size):
            # Classify building according to shake level
            # and calculate dollar losses

            if self.is_nexis:
                try:
                    area = float(attributes[i]['FLOOR_AREA'])
                except (ValueError, KeyError):
                    # print 'Got area', attributes[i]['FLOOR_AREA']
                    area = 0.0

                try:
                    building_value_density = float(attributes[i]['BUILDING_C'])
                except (ValueError, KeyError):
                    # print 'Got bld value', attributes[i]['BUILDING_C']
                    building_value_density = 0.0

                try:
                    contents_value_density = float(attributes[i]['CONTENTS_C'])
                except (ValueError, KeyError):
                    # print 'Got cont value', attributes[i]['CONTENTS_C']
                    contents_value_density = 0.0

                building_value = building_value_density * area
                contents_value = contents_value_density * area

            usage = get_osm_building_usage(attribute_names, attributes[i])
            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    if self.is_nexis:
                        self.affected_buildings[category][usage] = OrderedDict(
                            [
                                (tr('Buildings Affected'), 0),
                                (tr('Buildings value ($M)'), 0),
                                (tr('Contents value ($M)'), 0)])
                    else:
                        self.affected_buildings[category][usage] = OrderedDict(
                            [
                                (tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            try:
                mmi = float(attributes[i][hazard_attribute])  # MMI
            except TypeError:
                mmi = 0.0
            if t0 <= mmi < t1:
                cls = 1
                category = tr('Low')
            elif t1 <= mmi < t2:
                cls = 2
                category = tr('Medium')
            elif t2 <= mmi:
                cls = 3
                category = tr('High')
            else:
                # Not reported for less than level t0
                continue
            attributes[i][self.target_field] = cls
            self.affected_buildings[
                category][usage][tr('Buildings Affected')] += 1
            if self.is_nexis:
                self.affected_buildings[category][usage][
                    tr('Buildings value ($M)')] += building_value / 1000000.0
                self.affected_buildings[category][usage][
                    tr('Contents value ($M)')] += contents_value / 1000000.0

        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        impact_table = impact_summary = self.generate_html_report()

        # Create style
        style_classes = [dict(label=class_1['label'], value=class_1['class'],
                              colour='#ffff00', transparency=1),
                         dict(label=class_2['label'], value=class_2['class'],
                              colour='#ffaa00', transparency=1),
                         dict(label=class_3['label'], value=class_3['class'],
                              colour='#ff0000', transparency=1)]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Building affected by earthquake')
        legend_notes = tr('The level of the impact is according to the '
                          'threshold the user input.')
        legend_units = tr('(mmi)')
        legend_title = tr('Impact level')

        # Create vector layer and return
        result_layer = Vector(
            data=attributes,
            projection=interpolate_result.get_projection(),
            geometry=interpolate_result.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'map_title': map_title,
                'legend_notes': legend_notes,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'target_field': self.target_field,
                'statistics_type': self.statistics_type,
                'statistics_classes': self.statistics_classes},
            style_info=style_info)

        msg = 'Created vector layer %s' % str(result_layer)
        LOGGER.debug(msg)
        self._impact = result_layer
        return result_layer
Example #5
0
    def run(self):
        """Risk plugin for volcano hazard on building/structure.

        Counts number of building exposed to each volcano hazard zones.

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step', 'Impact function is calculating the impact.')

        # Get parameters from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.name_attribute = self.hazard.keyword('volcano_name_field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Input checks
        if not self.hazard.layer.is_polygon_data:
            message = (
                'Input hazard must be a polygon. I got %s with '
                'layer type %s' %
                (self.hazard.name, self.hazard.layer.get_geometry_name()))
            raise Exception(message)

        # Check if hazard_zone_attribute exists in hazard_layer
        if (self.hazard_class_attribute
                not in self.hazard.layer.get_attribute_names()):
            message = (
                'Hazard data %s did not contain expected attribute %s ' %
                (self.hazard.name, self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Get names of volcanoes considered
        if self.name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = set()
            for row in self.hazard.layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[self.name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)
        else:
            self.volcano_names = tr('Not specified in data')

        # Retrieve the classification that is used by the hazard layer.
        vector_hazard_classification = self.hazard.keyword(
            'vector_hazard_classification')
        # Get the dictionary that contains the definition of the classification
        vector_hazard_classification = definition(vector_hazard_classification)
        # Get the list classes in the classification
        vector_hazard_classes = vector_hazard_classification['classes']
        # Initialize OrderedDict of affected buildings
        self.affected_buildings = OrderedDict()
        # Iterate over vector hazard classes
        for vector_hazard_class in vector_hazard_classes:
            # Check if the key of class exist in hazard_class_mapping
            if vector_hazard_class['key'] in self.hazard_class_mapping.keys():
                # Replace the key with the name as we need to show the human
                # friendly name in the report.
                self.hazard_class_mapping[vector_hazard_class['name']] = \
                    self.hazard_class_mapping.pop(vector_hazard_class['key'])
                # Adding the class name as a key in affected_building
                self.affected_buildings[vector_hazard_class['name']] = {}

        # Run interpolation function for polygon2raster
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer, self.exposure.layer)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        self.buildings = {}

        for i in range(len(features)):
            # Get the hazard value based on the value mapping in keyword
            hazard_value = get_key_for_value(
                features[i][self.hazard_class_attribute],
                self.hazard_class_mapping)
            if not hazard_value:
                hazard_value = self._not_affected_value
            features[i][self.target_field] = get_string(hazard_value)

            if (self.exposure_class_attribute
                    and self.exposure_class_attribute in attribute_names):
                usage = features[i][self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, features[i])

            if usage in [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])

            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][tr(
                    'Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        colours = colours[::-1]  # flip

        colours = colours[:len(self.affected_buildings.keys())]

        style_classes = []

        i = 0
        for category_name in self.affected_buildings.keys():
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(self.affected_buildings.keys()):
                i = len(self.affected_buildings.keys()) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic hazard zone'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #6
0
    def run(self, layers=None):
        """Classified hazard impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard: Classified Hazard layer
                * exposure: Vector layer of structure data on
                the same grid as hazard
        """
        self.validate()
        self.prepare(layers)

        # The 3 classes
        low_t = self.parameters['low_hazard_class']
        medium_t = self.parameters['medium_hazard_class']
        high_t = self.parameters['high_hazard_class']

        # Extract data
        hazard = self.hazard  # Classified Hazard
        exposure = self.exposure  # Building locations

        # Determine attribute name for hazard levels
        if hazard.is_raster:
            hazard_attribute = 'level'
        else:
            hazard_attribute = None

        interpolated_result = assign_hazard_values_to_exposure_data(
            hazard, exposure, attribute_name=hazard_attribute, mode='constant')

        # Extract relevant exposure data
        attribute_names = interpolated_result.get_attribute_names()
        attributes = interpolated_result.get_data()

        buildings_total = len(interpolated_result)
        # Calculate building impact
        self.buildings = {}
        self.affected_buildings = OrderedDict([(tr('High Hazard Class'), {}),
                                               (tr('Medium Hazard Class'), {}),
                                               (tr('Low Hazard Class'), {})])
        for i in range(buildings_total):
            usage = get_osm_building_usage(attribute_names, attributes[i])
            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])

            # Count all buildings by type
            self.buildings[usage] += 1
            attributes[i][self.target_field] = 0
            attributes[i][self.affected_field] = 0
            level = float(attributes[i]['level'])
            level = float(numpy_round(level))
            if level == high_t:
                impact_level = tr('High Hazard Class')
            elif level == medium_t:
                impact_level = tr('Medium Hazard Class')
            elif level == low_t:
                impact_level = tr('Low Hazard Class')
            else:
                continue

            # Add calculated impact to existing attributes
            attributes[i][self.target_field] = {
                tr('High Hazard Class'): 3,
                tr('Medium Hazard Class'): 2,
                tr('Low Hazard Class'): 1
            }[impact_level]
            attributes[i][self.affected_field] = 1
            # Count affected buildings by type
            self.affected_buildings[impact_level][usage][tr(
                'Buildings Affected')] += 1

        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        # Create style
        style_classes = [
            dict(label=tr('High'),
                 value=3,
                 colour='#F31A1C',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Medium'),
                 value=2,
                 colour='#F4A442',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Low'),
                 value=1,
                 colour='#EBF442',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Not Affected'),
                 value=None,
                 colour='#1EFC7C',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2)
        ]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        impact_table = impact_summary = self.generate_html_report()
        # For printing map purpose
        map_title = tr('Buildings affected')
        legend_units = tr('(Low, Medium, High)')
        legend_title = tr('Structure inundated status')

        # Create vector layer and return
        vector_layer = Vector(data=attributes,
                              projection=exposure.get_projection(),
                              geometry=exposure.get_geometry(),
                              name=tr('Estimated buildings affected'),
                              keywords={
                                  'impact_summary':
                                  impact_summary,
                                  'impact_table':
                                  impact_table,
                                  'target_field':
                                  self.affected_field,
                                  'map_title':
                                  map_title,
                                  'legend_units':
                                  legend_units,
                                  'legend_title':
                                  legend_title,
                                  'buildings_total':
                                  buildings_total,
                                  'buildings_affected':
                                  self.total_affected_buildings
                              },
                              style_info=style_info)
        self._impact = vector_layer
        return vector_layer
Example #7
0
    def run(self):
        """Risk plugin for classified polygon hazard on building/structure.

        Counts number of building exposed to each hazard zones.

        :returns: Impact vector layer building exposed to each hazard zones.
            Table with number of buildings affected
        :rtype: Vector
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step', 'Impact function is calculating the impact.')

        # Value from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Retrieve the classification that is used by the hazard layer.
        vector_hazard_classification = self.hazard.keyword(
            'vector_hazard_classification')
        # Get the dictionary that contains the definition of the classification
        vector_hazard_classification = definition(vector_hazard_classification)
        # Get the list classes in the classification
        vector_hazard_classes = vector_hazard_classification['classes']
        # Initialize OrderedDict of affected buildings
        self.affected_buildings = OrderedDict()
        # Iterate over vector hazard classes
        for vector_hazard_class in vector_hazard_classes:
            # Check if the key of class exist in hazard_class_mapping
            if vector_hazard_class['key'] in self.hazard_class_mapping.keys():
                # Replace the key with the name as we need to show the human
                # friendly name in the report.
                self.hazard_class_mapping[vector_hazard_class['name']] = \
                    self.hazard_class_mapping.pop(vector_hazard_class['key'])
                # Adding the class name as a key in affected_building
                self.affected_buildings[vector_hazard_class['name']] = {}

        hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex(
            self.hazard_class_attribute)

        # Check if hazard_zone_attribute exists in hazard_layer
        if hazard_zone_attribute_index < 0:
            message = (
                'Hazard data %s does not contain expected attribute %s ' %
                (self.hazard.layer.name(), self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Hazard zone categories from hazard layer
        unique_values = self.hazard.layer.uniqueValues(
            hazard_zone_attribute_index)
        # Values might be integer or float, we should have unicode. #2626
        self.hazard_zones = [get_unicode(val) for val in unique_values]

        self.buildings = {}

        wgs84_extent = QgsRectangle(self.requested_extent[0],
                                    self.requested_extent[1],
                                    self.requested_extent[2],
                                    self.requested_extent[3])

        # Run interpolation function for polygon2polygon
        interpolated_layer = interpolate_polygon_polygon(
            self.hazard.layer, self.exposure.layer, wgs84_extent)

        new_field = QgsField(self.target_field, QVariant.String)
        interpolated_layer.dataProvider().addAttributes([new_field])
        interpolated_layer.updateFields()

        attribute_names = [
            field.name() for field in interpolated_layer.pendingFields()
        ]
        target_field_index = interpolated_layer.fieldNameIndex(
            self.target_field)
        changed_values = {}

        if interpolated_layer.featureCount() < 1:
            raise ZeroImpactException()

        # Extract relevant interpolated data
        for feature in interpolated_layer.getFeatures():
            # Get the hazard value based on the value mapping in keyword
            hazard_value = get_key_for_value(
                feature[self.hazard_class_attribute],
                self.hazard_class_mapping)
            if not hazard_value:
                hazard_value = self._not_affected_value
            changed_values[feature.id()] = {target_field_index: hazard_value}

            if (self.exposure_class_attribute
                    and self.exposure_class_attribute in attribute_names):
                usage = feature[self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, feature)

            if usage is None:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.hazard_class_mapping.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])
            self.buildings[usage] += 1
            if hazard_value in self.hazard_class_mapping.keys():
                self.affected_buildings[hazard_value][usage][tr(
                    'Buildings Affected')] += 1

        interpolated_layer.dataProvider().changeAttributeValues(changed_values)

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        categories = self.affected_buildings.keys()
        categories.append(self._not_affected_value)
        colours = color_ramp(len(categories))
        style_classes = []

        i = 0
        for hazard_zone in self.affected_buildings.keys():
            style_class = dict()
            style_class['label'] = tr(hazard_zone)
            style_class['transparency'] = 0
            style_class['value'] = hazard_zone
            style_class['size'] = 1
            style_class['colour'] = colours[i]
            style_classes.append(style_class)
            i += 1

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by each hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Vector(
            data=interpolated_layer,
            name=tr('Buildings affected by each hazard zone'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #8
0
    def run(self, layers=None):
        """Risk plugin for classified polygon hazard on building/structure.

        Counts number of building exposed to each hazard zones.

        :param layers: List of layers expected to contain.
                * hazard_layer: Hazard layer
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer

        :returns: Map of building exposed to each hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare(layers)

        # Target Field
        target_field = 'zone'

        # Not affected string in the target field
        not_affected_value = 'Not Affected'

        # Parameters
        hazard_zone_attribute = self.parameters['hazard zone attribute']

        # Identify hazard and exposure layers
        hazard_layer = self.hazard
        exposure_layer = self.exposure

        # Input checks
        if not hazard_layer.is_polygon_data:
            message = (
                'Input hazard must be a polygon. I got %s with '
                'layer type %s' %
                (hazard_layer.get_name(), hazard_layer.get_geometry_name()))
            raise Exception(message)

        # Check if hazard_zone_attribute exists in hazard_layer
        if hazard_zone_attribute not in hazard_layer.get_attribute_names():
            message = (
                'Hazard data %s does not contain expected attribute %s ' %
                (hazard_layer.get_name(), hazard_zone_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Find the target field name that has no conflict with default
        # target
        attribute_names = hazard_layer.get_attribute_names()
        target_field = get_non_conflicting_attribute_name(
            target_field, attribute_names)

        # Hazard zone categories from hazard layer
        self.hazard_zones = list(
            set(hazard_layer.get_data(hazard_zone_attribute)))

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for hazard_zone in self.hazard_zones:
            self.affected_buildings[hazard_zone] = {}

        # Run interpolation function for polygon2polygon
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=None)

        # Extract relevant interpolated data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        for i in range(len(features)):
            hazard_value = features[i][hazard_zone_attribute]
            if not hazard_value:
                hazard_value = not_affected_value
            features[i][target_field] = hazard_value
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is None:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict(
                        [(tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.generate_html_report()

        # Create style
        categories = self.hazard_zones
        categories.append(not_affected_value)
        colours = color_ramp(len(categories))
        style_classes = []

        i = 0
        for hazard_zone in self.hazard_zones:
            style_class = dict()
            style_class['label'] = tr(hazard_zone)
            style_class['transparency'] = 0
            style_class['value'] = hazard_zone
            style_class['size'] = 1
            style_class['colour'] = colours[i]
            style_classes.append(style_class)
            i += 1

        # Override style info with new classes and name
        style_info = dict(target_field=target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by each hazard zone')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(building)')
        legend_title = tr('Building count')

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by each hazard zone'),
            keywords={'impact_summary': impact_summary,
                      'impact_table': impact_table,
                      'target_field': target_field,
                      'map_title': map_title,
                      'legend_notes': legend_notes,
                      'legend_units': legend_units,
                      'legend_title': legend_title},
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #9
0
    def run(self, layers=None):
        """Flood impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard_layer: Hazard raster layer of flood
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer
        """
        self.validate()
        self.prepare(layers)

        threshold = self.parameters['threshold [m]']  # Flood threshold [m]

        verify(isinstance(threshold, float),
               'Expected thresholds to be a float. Got %s' % str(threshold))

        # Extract data
        hazard_layer = self.hazard  # Depth
        exposure_layer = self.exposure  # Building locations

        # Determine attribute name for hazard levels
        hazard_attribute = 'depth'

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (tr('Number Inundated'), {}),
            (tr('Number of Wet Buildings'), {}),
            (tr('Number of Dry Buildings'), {})
        ])
        for i in range(total_features):
            # Get the interpolated depth
            water_depth = float(features[i]['depth'])
            if water_depth <= 0:
                inundated_status = 0  # dry
            elif water_depth >= threshold:
                inundated_status = 1  # inundated
            else:
                inundated_status = 2  # wet

            # Count affected buildings by usage type if available
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            # Add calculated impact to existing attributes
            features[i][self.target_field] = inundated_status
            category = [
                tr('Number of Dry Buildings'),
                tr('Number of Wet Buildings'),
                tr('Number Inundated')][inundated_status]
            self.affected_buildings[category][usage][
                tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()
        # Generate simple impact report
        impact_table = impact_summary = self.generate_html_report()

        # Prepare impact layer
        map_title = tr('Buildings inundated')
        legend_title = tr('Structure inundated status')

        style_classes = [
            dict(
                label=tr('Dry (<= 0 m)'),
                value=0,
                colour='#1EFC7C',
                transparency=0,
                size=1
            ),
            dict(
                label=tr('Wet (0 m - %.1f m)') % threshold,
                value=2,
                colour='#FF9900',
                transparency=0,
                size=1
            ),
            dict(
                label=tr('Inundated (>= %.1f m)') % threshold,
                value=1,
                colour='#F31A1C',
                transparency=0,
                size=1
            )]
        legend_units = tr('(inundated, wet, or dry)')

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # Create vector layer and return
        vector_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': self.target_field,
                'map_title': map_title,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'buildings_total': total_features,
                'buildings_affected': self.total_affected_buildings},
            style_info=style_info)
        self._impact = vector_layer
        return vector_layer
Example #10
0
    def run(self):
        """Risk plugin for volcano hazard on building/structure.

        Counts number of building exposed to each volcano hazard zones.

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        # Get parameters from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        name_attribute = self.hazard.keyword('volcano_name_field')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Input checks
        if not self.hazard.layer.is_polygon_data:
            message = (
                'Input hazard must be a polygon. I got %s with '
                'layer type %s' %
                (self.hazard.name, self.hazard.layer.get_geometry_name()))
            raise Exception(message)

        # Check if hazard_zone_attribute exists in hazard_layer
        if (self.hazard_class_attribute not in
                self.hazard.layer.get_attribute_names()):
            message = (
                'Hazard data %s did not contain expected attribute %s ' %
                (self.hazard.name, self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Get names of volcanoes considered
        if name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = set()
            for row in self.hazard.layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)
        else:
            self.volcano_names = tr('Not specified in data')

        # Run interpolation function for polygon2raster
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer, self.exposure.layer)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        # Hazard zone categories from hazard layer
        hazard_zone_categories = list(
            set(self.hazard.layer.get_data(self.hazard_class_attribute)))

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for hazard_category in hazard_zone_categories:
            self.affected_buildings[hazard_category] = {}

        for i in range(len(features)):
            hazard_value = features[i][self.hazard_class_attribute]
            if not hazard_value:
                hazard_value = self._not_affected_value
            features[i][self.target_field] = hazard_value
            if (self.exposure_class_attribute and
                    self.exposure_class_attribute in attribute_names):
                usage = features[i][self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, features[i])
            if usage in [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][
                        usage] = OrderedDict([
                            (tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()
        category_names = hazard_zone_categories
        category_names.append(self._not_affected_value)

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        colours = colours[::-1]  # flip

        colours = colours[:len(category_names)]

        style_classes = []

        i = 0
        for category_name in category_names:
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(category_names):
                i = len(category_names) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic hazard zone'),
            keywords={'impact_summary': impact_summary,
                      'impact_table': impact_table,
                      'target_field': self.target_field,
                      'map_title': map_title,
                      'legend_notes': legend_notes,
                      'legend_units': legend_units,
                      'legend_title': legend_title},
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
    def run(self, layers):
        """Flood impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard_layer: Hazard raster layer of flood
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer
        """
        threshold = self.parameters['threshold [m]']  # Flood threshold [m]

        verify(isinstance(threshold, float),
               'Expected thresholds to be a float. Got %s' % str(threshold))

        # Extract data
        hazard_layer = get_hazard_layer(layers)  # Depth
        exposure_layer = get_exposure_layer(layers)  # Building locations

        question = get_question(hazard_layer.get_name(),
                                exposure_layer.get_name(), self)

        # Determine attribute name for hazard levels
        mode = 'grid'
        hazard_attribute = 'depth'

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)
        buildings = {}

        # The number of affected buildings
        affected_count = 0

        # The variable for grid mode
        inundated_count = 0
        wet_count = 0
        dry_count = 0
        inundated_buildings = {}
        wet_buildings = {}
        dry_buildings = {}

        # The variable for regions mode
        affected_buildings = {}

        for i in range(total_features):
            # Get the interpolated depth
            water_depth = float(features[i]['depth'])
            if water_depth <= 0:
                inundated_status = 0  # dry
            elif water_depth >= threshold:
                inundated_status = 1  # inundated
            else:
                inundated_status = 2  # wet

            # Count affected buildings by usage type if available
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is not None and usage != 0:
                key = usage
            else:
                key = 'unknown'

            if key not in buildings:
                buildings[key] = 0
                inundated_buildings[key] = 0
                wet_buildings[key] = 0
                dry_buildings[key] = 0

            # Count all buildings by type
            buildings[key] += 1
            if inundated_status is 0:
                # Count dry buildings by type
                dry_buildings[key] += 1
                # Count total dry buildings
                dry_count += 1
            if inundated_status is 1:
                # Count inundated buildings by type
                inundated_buildings[key] += 1
                # Count total dry buildings
                inundated_count += 1
            if inundated_status is 2:
                # Count wet buildings by type
                wet_buildings[key] += 1
                # Count total wet buildings
                wet_count += 1
            # Add calculated impact to existing attributes
            features[i][self.target_field] = inundated_status

        affected_count = inundated_count + wet_count

        # Lump small entries and 'unknown' into 'other' category
        for usage in buildings.keys():
            x = buildings[usage]
            if x < 25 or usage == 'unknown':
                if 'other' not in buildings:
                    buildings['other'] = 0
                    if mode == 'grid':
                        inundated_buildings['other'] = 0
                        wet_buildings['other'] = 0
                        dry_buildings['other'] = 0
                    elif mode == 'regions':
                        affected_buildings['other'] = 0

                buildings['other'] += x
                if mode == 'grid':
                    inundated_buildings['other'] += inundated_buildings[usage]
                    wet_buildings['other'] += wet_buildings[usage]
                    dry_buildings['other'] += dry_buildings[usage]
                    del buildings[usage]
                    del inundated_buildings[usage]
                    del wet_buildings[usage]
                    del dry_buildings[usage]
                elif mode == 'regions':
                    affected_buildings['other'] += affected_buildings[usage]
                    del buildings[usage]
                    del affected_buildings[usage]

        # Generate simple impact report
        table_body = [
            question,
            TableRow([
                tr('Building type'),
                tr('Number Inundated'),
                tr('Number of Wet Buildings'),
                tr('Number of Dry Buildings'),
                tr('Total')
            ],
                     header=True),
            TableRow([
                tr('All'),
                format_int(inundated_count),
                format_int(wet_count),
                format_int(dry_count),
                format_int(total_features)
            ])
        ]

        school_closed = 0
        hospital_closed = 0
        # Generate break down by building usage type if available
        list_type_attribute = [
            'TYPE', 'type', 'amenity', 'building_t', 'office', 'tourism',
            'leisure', 'building'
        ]
        intersect_type = set(attribute_names) & set(list_type_attribute)
        if len(intersect_type) > 0:
            # Make list of building types
            building_list = []
            for usage in buildings:
                building_type = usage.replace('_', ' ')

                # Lookup internationalised value if available
                building_type = tr(building_type)
                building_list.append([
                    building_type.capitalize(),
                    format_int(inundated_buildings[usage]),
                    format_int(wet_buildings[usage]),
                    format_int(dry_buildings[usage]),
                    format_int(buildings[usage])
                ])

                if usage.lower() == 'school':
                    school_closed = 0
                    school_closed += inundated_buildings[usage]
                    school_closed += wet_buildings[usage]
                if usage.lower() == 'hospital':
                    hospital_closed = 0
                    hospital_closed += inundated_buildings[usage]
                    hospital_closed += wet_buildings[usage]

            # Sort alphabetically
            building_list.sort()

            table_body.append(
                TableRow(tr('Breakdown by building type'), header=True))
            for row in building_list:
                s = TableRow(row)
                table_body.append(s)

        # Action Checklist Section
        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(
            TableRow(tr('Are the critical facilities still open?')))
        table_body.append(
            TableRow(
                tr('Which structures have warning capacity (eg. sirens, speakers, '
                   'etc.)?')))
        table_body.append(
            TableRow(tr('Which buildings will be evacuation centres?')))
        table_body.append(
            TableRow(tr('Where will we locate the operations centre?')))
        table_body.append(
            TableRow(
                tr('Where will we locate warehouse and/or distribution centres?'
                   )))

        if school_closed > 0:
            table_body.append(
                TableRow(
                    tr('Where will the students from the %s closed schools go to '
                       'study?') % format_int(school_closed)))

        if hospital_closed > 0:
            table_body.append(
                TableRow(
                    tr('Where will the patients from the %s closed hospitals go '
                       'for treatment and how will we transport them?') %
                    format_int(hospital_closed)))

        # Notes Section
        table_body.append(TableRow(tr('Notes'), header=True))
        table_body.append(
            TableRow(
                tr('Buildings are said to be inundated when flood levels '
                   'exceed %.1f m') % threshold))
        table_body.append(
            TableRow(
                tr('Buildings are said to be wet when flood levels '
                   'are greater than 0 m but less than %.1f m') % threshold))
        table_body.append(
            TableRow(
                tr('Buildings are said to be dry when flood levels '
                   'are less than 0 m')))
        table_body.append(
            TableRow(
                tr('Buildings are said to be closed if they are inundated or '
                   'wet')))
        table_body.append(
            TableRow(tr('Buildings are said to be open if they are dry')))

        # Result
        impact_summary = Table(table_body).toNewlineFreeString()
        impact_table = impact_summary

        # Prepare impact layer
        map_title = tr('Buildings inundated')
        legend_title = tr('Structure inundated status')

        style_classes = [
            dict(label=tr('Dry (<= 0 m)'),
                 value=0,
                 colour='#1EFC7C',
                 transparency=0,
                 size=1),
            dict(label=tr('Wet (0 m - %.1f m)') % threshold,
                 value=2,
                 colour='#FF9900',
                 transparency=0,
                 size=1),
            dict(label=tr('Inundated (>= %.1f m)') % threshold,
                 value=1,
                 colour='#F31A1C',
                 transparency=0,
                 size=1)
        ]
        legend_units = tr('(inundated, wet, or dry)')

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # Create vector layer and return
        vector_layer = Vector(data=features,
                              projection=interpolated_layer.get_projection(),
                              geometry=interpolated_layer.get_geometry(),
                              name=tr('Estimated buildings affected'),
                              keywords={
                                  'impact_summary': impact_summary,
                                  'impact_table': impact_table,
                                  'target_field': self.target_field,
                                  'map_title': map_title,
                                  'legend_units': legend_units,
                                  'legend_title': legend_title,
                                  'buildings_total': total_features,
                                  'buildings_affected': affected_count
                              },
                              style_info=style_info)
        return vector_layer
    def run(self):
        """Classified hazard impact to buildings (e.g. from Open Street Map).
        """
        self.validate()
        self.prepare()

        self.provenance.append_step("Calculating Step", "Impact function is calculating the impact.")

        # Value from layer's keywords
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword("structure_class_field")
        except KeywordNotFoundError:
            structure_class_field = None

        # The 3 classes
        categorical_hazards = self.parameters["Categorical hazards"].value
        low_t = categorical_hazards[0].value
        medium_t = categorical_hazards[1].value
        high_t = categorical_hazards[2].value

        # Determine attribute name for hazard levels
        if self.hazard.layer.is_raster:
            hazard_attribute = "level"
        else:
            hazard_attribute = None

        interpolated_result = assign_hazard_values_to_exposure_data(
            self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute, mode="constant"
        )

        # Extract relevant exposure data
        attribute_names = interpolated_result.get_attribute_names()
        attributes = interpolated_result.get_data()

        buildings_total = len(interpolated_result)
        # Calculate building impact
        self.buildings = {}
        self.affected_buildings = OrderedDict(
            [(tr("High Hazard Class"), {}), (tr("Medium Hazard Class"), {}), (tr("Low Hazard Class"), {})]
        )
        for i in range(buildings_total):

            if structure_class_field and structure_class_field in attribute_names:
                usage = attributes[i][structure_class_field]
            else:
                usage = get_osm_building_usage(attribute_names, attributes[i])

            if usage is None or usage == 0:
                usage = "unknown"

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([(tr("Buildings Affected"), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            attributes[i][self.target_field] = 0
            attributes[i][self.affected_field] = 0
            level = float(attributes[i]["level"])
            level = float(numpy_round(level))
            if level == high_t:
                impact_level = tr("High Hazard Class")
            elif level == medium_t:
                impact_level = tr("Medium Hazard Class")
            elif level == low_t:
                impact_level = tr("Low Hazard Class")
            else:
                continue

            # Add calculated impact to existing attributes
            attributes[i][self.target_field] = {
                tr("High Hazard Class"): 3,
                tr("Medium Hazard Class"): 2,
                tr("Low Hazard Class"): 1,
            }[impact_level]
            attributes[i][self.affected_field] = 1
            # Count affected buildings by type
            self.affected_buildings[impact_level][usage][tr("Buildings Affected")] += 1

        # Consolidate the small building usage groups < 25 to other
        # Building threshold #2468
        postprocessors = self.parameters["postprocessors"]
        building_postprocessors = postprocessors["BuildingType"][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Create style
        style_classes = [
            dict(
                label=tr("High"),
                value=3,
                colour="#F31A1C",
                transparency=0,
                size=2,
                border_color="#969696",
                border_width=0.2,
            ),
            dict(
                label=tr("Medium"),
                value=2,
                colour="#F4A442",
                transparency=0,
                size=2,
                border_color="#969696",
                border_width=0.2,
            ),
            dict(
                label=tr("Low"),
                value=1,
                colour="#EBF442",
                transparency=0,
                size=2,
                border_color="#969696",
                border_width=0.2,
            ),
            dict(
                label=tr("Not Affected"),
                value=None,
                colour="#1EFC7C",
                transparency=0,
                size=2,
                border_color="#969696",
                border_width=0.2,
            ),
        ]
        style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type="categorizedSymbol")

        impact_table = impact_summary = self.html_report()

        # For printing map purpose
        map_title = tr("Buildings affected")
        legend_title = tr("Structure inundated status")
        legend_units = tr("(Low, Medium, High)")

        extra_keywords = {
            "impact_summary": impact_summary,
            "impact_table": impact_table,
            "target_field": self.affected_field,
            "map_title": map_title,
            "legend_units": legend_units,
            "legend_title": legend_title,
            "buildings_total": buildings_total,
            "buildings_affected": self.total_affected_buildings,
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        vector_layer = Vector(
            data=attributes,
            projection=self.exposure.layer.get_projection(),
            geometry=self.exposure.layer.get_geometry(),
            name=tr("Estimated buildings affected"),
            keywords=impact_layer_keywords,
            style_info=style_info,
        )
        self._impact = vector_layer
        return vector_layer
Example #13
0
    def run(self):
        """Earthquake impact to buildings (e.g. from OpenStreetMap)."""
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step', 'Impact function is calculating the impact.')

        LOGGER.debug('Running earthquake building impact')

        # merely initialize
        building_value = 0
        contents_value = 0

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

        # Class Attribute and Label.

        class_1 = {'label': tr('Low'), 'class': 1}
        class_2 = {'label': tr('Medium'), 'class': 2}
        class_3 = {'label': tr('High'), 'class': 3}

        # Define attribute name for hazard levels.
        hazard_attribute = 'mmi'

        # Determine if exposure data have NEXIS attributes.
        attribute_names = self.exposure.layer.get_attribute_names()
        if ('FLOOR_AREA' in attribute_names and 'BUILDING_C' in attribute_names
                and 'CONTENTS_C' in attribute_names):
            self.is_nexis = True
        else:
            self.is_nexis = False

        # Interpolate hazard level to building locations.
        interpolate_result = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute)

        # Extract relevant exposure data
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None
        attributes = interpolate_result.get_data()

        interpolate_size = len(interpolate_result)

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (tr('High'), {}),
            (tr('Medium'), {}),
            (tr('Low'), {}),
        ])
        removed = []
        for i in range(interpolate_size):
            # Classify building according to shake level
            # and calculate dollar losses

            if self.is_nexis:
                try:
                    area = float(attributes[i]['FLOOR_AREA'])
                except (ValueError, KeyError):
                    # print 'Got area', attributes[i]['FLOOR_AREA']
                    area = 0.0

                try:
                    building_value_density = float(attributes[i]['BUILDING_C'])
                except (ValueError, KeyError):
                    # print 'Got bld value', attributes[i]['BUILDING_C']
                    building_value_density = 0.0

                try:
                    contents_value_density = float(attributes[i]['CONTENTS_C'])
                except (ValueError, KeyError):
                    # print 'Got cont value', attributes[i]['CONTENTS_C']
                    contents_value_density = 0.0

                building_value = building_value_density * area
                contents_value = contents_value_density * area

            if (structure_class_field in attribute_names
                    and structure_class_field):
                usage = attributes[i].get(structure_class_field, None)
            else:
                usage = get_osm_building_usage(attribute_names, attributes[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    if self.is_nexis:
                        self.affected_buildings[category][usage] = OrderedDict(
                            [(tr('Buildings Affected'), 0),
                             (tr('Buildings value ($M)'), 0),
                             (tr('Contents value ($M)'), 0)])
                    else:
                        self.affected_buildings[category][usage] = \
                            OrderedDict([(tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            try:
                mmi = float(attributes[i][hazard_attribute])  # MMI
            except TypeError:
                mmi = 0.0
            if t0 <= mmi < t1:
                cls = 1
                category = tr('Low')
            elif t1 <= mmi < t2:
                cls = 2
                category = tr('Medium')
            elif t2 <= mmi:
                cls = 3
                category = tr('High')
            else:
                # Not reported for less than level t0
                continue

            attributes[i][self.target_field] = cls
            self.affected_buildings[category][usage][tr(
                'Buildings Affected')] += 1
            if self.is_nexis:
                self.affected_buildings[category][usage][tr(
                    'Buildings value ($M)')] += building_value / 1000000.0
                self.affected_buildings[category][usage][tr(
                    'Contents value ($M)')] += contents_value / 1000000.0

        # remove un-categorized element
        removed.reverse()
        geometry = interpolate_result.get_geometry()
        for i in range(0, len(removed)):
            del attributes[removed[i]]
            del geometry[removed[i]]

        if len(attributes) < 1:
            raise ZeroImpactException()
        # Consolidate the small building usage groups < 25 to other
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        impact_table = impact_summary = self.html_report()

        # Create style
        style_classes = [
            dict(label=class_1['label'],
                 value=class_1['class'],
                 colour='#ffff00',
                 transparency=1),
            dict(label=class_2['label'],
                 value=class_2['class'],
                 colour='#ffaa00',
                 transparency=1),
            dict(label=class_3['label'],
                 value=class_3['class'],
                 colour='#ff0000',
                 transparency=1)
        ]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Building affected by earthquake')
        legend_notes = tr(
            'The level of the impact is according to the threshold the user '
            'input.')
        legend_units = tr('(mmi)')
        legend_title = tr('Impact level')

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title,
            'target_field': self.target_field,
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        result_layer = Vector(data=attributes,
                              projection=interpolate_result.get_projection(),
                              geometry=geometry,
                              name=tr('Estimated buildings affected'),
                              keywords=impact_layer_keywords,
                              style_info=style_info)

        msg = 'Created vector layer %s' % str(result_layer)
        LOGGER.debug(msg)
        self._impact = result_layer
        return result_layer
Example #14
0
    def run(self, layers):
        """Flood impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard_layer: Hazard raster layer of flood
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer
        """

        # Extract data
        hazard_layer = get_hazard_layer(layers)  # Depth
        exposure_layer = get_exposure_layer(layers)  # Building locations

        question = get_question(hazard_layer.get_name(),
                                exposure_layer.get_name(), self)

        # Determine attribute name for hazard levels
        hazard_attribute = None

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)
        buildings = {}

        # The number of affected buildings
        affected_count = 0

        # The variable for regions mode
        affected_buildings = {}

        for i in range(total_features):
            # Use interpolated polygon attribute
            atts = features[i]

            # FIXME (Ole): Need to agree whether to use one or the
            # other as this can be very confusing!
            # For now look for 'affected' first
            if 'affected' in atts:
                # E.g. from flood forecast
                # Assume that building is wet if inside polygon
                # as flagged by attribute Flooded
                res = atts['affected']
                if res is None:
                    inundated_status = False
                else:
                    inundated_status = bool(res)
            elif 'FLOODPRONE' in atts:
                res = atts['FLOODPRONE']
                if res is None:
                    inundated_status = False
                else:
                    inundated_status = res.lower() == 'yes'
            elif DEFAULT_ATTRIBUTE in atts:
                # Check the default attribute assigned for points
                # covered by a polygon
                res = atts[DEFAULT_ATTRIBUTE]
                if res is None:
                    inundated_status = False
                else:
                    inundated_status = res
            else:
                # there is no flood related attribute
                message = ('No flood related attribute found in %s. I was '
                           'looking for either "affected", "FLOODPRONE" or '
                           '"inapolygon". The latter should have been '
                           'automatically set by call to '
                           'assign_hazard_values_to_exposure_data(). Sorry I '
                           'can\'t help more.')
                raise Exception(message)

            # Count affected buildings by usage type if available
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is not None and usage != 0:
                key = usage
            else:
                key = 'unknown'

            if key not in buildings:
                buildings[key] = 0
                affected_buildings[key] = 0

            # Count all buildings by type
            buildings[key] += 1
            if inundated_status is True:
                # Count affected buildings by type
                affected_buildings[key] += 1
                # Count total affected buildings
                affected_count += 1

            # Add calculated impact to existing attributes
            features[i][self.target_field] = int(inundated_status)

        # Lump small entries and 'unknown' into 'other' category
        for usage in buildings.keys():
            x = buildings[usage]
            if x < 25 or usage == 'unknown':
                if 'other' not in buildings:
                    buildings['other'] = 0
                    affected_buildings['other'] = 0

                buildings['other'] += x
                affected_buildings['other'] += affected_buildings[usage]
                del buildings[usage]
                del affected_buildings[usage]

        # Generate simple impact report
        table_body = [
            question,
            TableRow([tr('Building type'),
                      tr('Number flooded'),
                      tr('Total')],
                     header=True),
            TableRow([
                tr('All'),
                format_int(affected_count),
                format_int(total_features)
            ])
        ]

        school_closed = 0
        hospital_closed = 0
        # Generate break down by building usage type if available
        list_type_attribute = [
            'TYPE', 'type', 'amenity', 'building_t', 'office', 'tourism',
            'leisure', 'building'
        ]
        intersect_type = set(attribute_names) & set(list_type_attribute)
        if len(intersect_type) > 0:
            # Make list of building types
            building_list = []
            for usage in buildings:
                building_type = usage.replace('_', ' ')

                # Lookup internationalised value if available
                building_type = tr(building_type)
                building_list.append([
                    building_type.capitalize(),
                    format_int(affected_buildings[usage]),
                    format_int(buildings[usage])
                ])

                if usage.lower() == 'school':
                    school_closed = affected_buildings[usage]
                if usage.lower() == 'hospital':
                    hospital_closed = affected_buildings[usage]

            # Sort alphabetically
            building_list.sort()

            table_body.append(
                TableRow(tr('Breakdown by building type'), header=True))
            for row in building_list:
                s = TableRow(row)
                table_body.append(s)

        # Action Checklist Section
        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(
            TableRow(tr('Are the critical facilities still open?')))
        table_body.append(
            TableRow(
                tr('Which structures have warning capacity (eg. sirens, speakers, '
                   'etc.)?')))
        table_body.append(
            TableRow(tr('Which buildings will be evacuation centres?')))
        table_body.append(
            TableRow(tr('Where will we locate the operations centre?')))
        table_body.append(
            TableRow(
                tr('Where will we locate warehouse and/or distribution centres?'
                   )))

        if school_closed > 0:
            table_body.append(
                TableRow(
                    tr('Where will the students from the %s closed schools go to '
                       'study?') % format_int(school_closed)))

        if hospital_closed > 0:
            table_body.append(
                TableRow(
                    tr('Where will the patients from the %s closed hospitals go '
                       'for treatment and how will we transport them?') %
                    format_int(hospital_closed)))

        # Notes Section
        table_body.append(TableRow(tr('Notes'), header=True))
        table_body.append(
            TableRow(
                tr('Buildings are said to be flooded when in regions marked '
                   'as affected')))

        # Result
        impact_summary = Table(table_body).toNewlineFreeString()
        impact_table = impact_summary

        # Prepare impact layer
        map_title = tr('Buildings inundated')
        legend_title = tr('Structure inundated status')

        style_classes = [
            dict(label=tr('Not Inundated'),
                 value=0,
                 colour='#1EFC7C',
                 transparency=0,
                 size=1),
            dict(label=tr('Inundated'),
                 value=1,
                 colour='#F31A1C',
                 ztransparency=0,
                 size=1)
        ]
        legend_units = tr('(inundated or not inundated)')

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # Create vector layer and return
        vector_layer = Vector(data=features,
                              projection=interpolated_layer.get_projection(),
                              geometry=interpolated_layer.get_geometry(),
                              name=tr('Estimated buildings affected'),
                              keywords={
                                  'impact_summary': impact_summary,
                                  'impact_table': impact_table,
                                  'target_field': self.target_field,
                                  'map_title': map_title,
                                  'legend_units': legend_units,
                                  'legend_title': legend_title,
                                  'buildings_total': total_features,
                                  'buildings_affected': affected_count
                              },
                              style_info=style_info)
        return vector_layer
Example #15
0
    def run(self):
        """Counts number of building exposed to each volcano hazard zones.

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step',
            'Impact function is calculating the impact.')

        # Hazard Zone Attribute
        hazard_zone_attribute = 'radius'

        # Parameters
        radii = self.parameters['distances'].value

        # Get parameters from layer's keywords
        volcano_name_attribute = self.hazard.keyword('volcano_name_field')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Input checks
        if not self.hazard.layer.is_point_data:
            message = (
                'Input hazard must be a vector point layer. I got %s '
                'with layer type %s' % (
                    self.hazard.name, self.hazard.layer.get_geometry_name()))
            raise Exception(message)

        # Make hazard layer by buffering the point
        centers = self.hazard.layer.get_geometry()
        features = self.hazard.layer.get_data()
        hazard_layer = buffer_points(
            centers,
            radii,
            hazard_zone_attribute,
            data_table=features)
        # Category names for the impact zone
        category_names = radii
        # In kilometers
        self._affected_categories_volcano = [
            tr('Radius %.1f km') % key for key in radii[::]]

        # Get names of volcanoes considered
        if volcano_name_attribute in hazard_layer.get_attribute_names():
            volcano_name_list = set()
            for row in hazard_layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[volcano_name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)

        # Find the target field name that has no conflict with the attribute
        # names in the hazard layer
        hazard_attribute_names = hazard_layer.get_attribute_names()
        target_field = get_non_conflicting_attribute_name(
            self.target_field, hazard_attribute_names)

        # Run interpolation function for polygon2polygon
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, self.exposure.layer)

        # Extract relevant interpolated layer data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for category in radii:
            self.affected_buildings[category] = {}

        # Iterate the interpolated building layer
        for i in range(len(features)):
            hazard_value = features[i][hazard_zone_attribute]
            if not hazard_value:
                hazard_value = self._not_affected_value
            features[i][target_field] = hazard_value

            # Count affected buildings by usage type if available
            if (self.exposure_class_attribute and
                    self.exposure_class_attribute in attribute_names):
                usage = features[i][self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, features[i])

            if usage is [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][
                        usage] = OrderedDict([
                            (tr('Buildings Affected'), 0)])

            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Adding 'km'
        affected_building_keys = self.affected_buildings.keys()
        for key in affected_building_keys:
            self.affected_buildings[tr('Radius %.1f km' % key)] = \
                self.affected_buildings.pop(key)

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        colours = colours[::-1]  # flip
        colours = colours[:len(category_names)]
        style_classes = []

        i = 0
        for category_name in category_names:
            style_class = dict()
            style_class['label'] = tr('Radius %s km') % tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(category_names):
                i = len(category_names) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(
            target_field=target_field,
            style_classes=style_classes,
            style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic buffered point')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr(
            'Thousand separator is represented by %s' %
            get_thousand_separator())

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': target_field,
            'map_title': map_title,
            'legend_notes': legend_notes,
            'legend_units': legend_units,
            'legend_title': legend_title
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic buffered point'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #16
0
    def run(self):
        """Flood impact to buildings (e.g. from Open Street Map)."""
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step', 'Impact function is calculating the impact.')

        threshold = self.parameters['threshold'].value  # Flood threshold [m]

        verify(isinstance(threshold, float),
               'Expected thresholds to be a float. Got %s' % str(threshold))

        # Determine attribute name for hazard levels
        hazard_attribute = 'depth'

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)

        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([(tr('Flooded'), {}),
                                               (tr('Wet'), {}),
                                               (tr('Dry'), {})])
        for i in range(total_features):
            # Get the interpolated depth
            water_depth = float(features[i]['depth'])
            if water_depth <= 0:
                inundated_status = 0  # dry
            elif water_depth >= threshold:
                inundated_status = 1  # inundated
            else:
                inundated_status = 2  # wet

            # Count affected buildings by usage type if available
            if (structure_class_field in attribute_names
                    and structure_class_field):
                usage = features[i].get(structure_class_field, None)
            else:
                usage = get_osm_building_usage(attribute_names, features[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])

            # Count all buildings by type
            self.buildings[usage] += 1
            # Add calculated impact to existing attributes
            features[i][self.target_field] = inundated_status
            category = [tr('Dry'), tr('Flooded'), tr('Wet')][inundated_status]
            self.affected_buildings[category][usage][tr(
                'Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()
        # Generate simple impact report
        impact_table = impact_summary = self.html_report()

        # For printing map purpose
        map_title = tr('Flooded buildings')
        legend_title = tr('Flooded structure status')
        legend_units = tr('(flooded, wet, or dry)')

        style_classes = [
            dict(label=tr('Dry (<= 0 m)'),
                 value=0,
                 colour='#1EFC7C',
                 transparency=0,
                 size=1),
            dict(label=tr('Wet (0 m - %.1f m)') % threshold,
                 value=2,
                 colour='#FF9900',
                 transparency=0,
                 size=1),
            dict(label=tr('Flooded (>= %.1f m)') % threshold,
                 value=1,
                 colour='#F31A1C',
                 transparency=0,
                 size=1)
        ]

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.target_field,
            'map_title': map_title,
            'legend_title': legend_title,
            'legend_units': legend_units,
            'buildings_total': total_features,
            'buildings_affected': self.total_affected_buildings
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        vector_layer = Vector(data=features,
                              projection=interpolated_layer.get_projection(),
                              geometry=interpolated_layer.get_geometry(),
                              name=tr('Estimated buildings affected'),
                              keywords=impact_layer_keywords,
                              style_info=style_info)
        # Create vector layer and return
        self._impact = vector_layer
        return vector_layer
Example #17
0
    def run(self, layers=None):
        """Counts number of building exposed to each volcano hazard zones.

        :param layers: List of layers expected to contain.
                * hazard_layer: Hazard layer of volcano
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare(layers)
        # Target Field
        target_field = 'zone'
        # Hazard Zone Attribute
        hazard_zone_attribute = 'radius'
        # Not Affected Value
        not_affected_value = 'Not Affected'

        # Parameters
        radii = self.parameters['distances [km]']
        volcano_name_attribute = self.parameters['volcano name attribute']

        # Identify hazard and exposure layers
        hazard_layer = self.hazard  # Volcano hazard layer
        exposure_layer = self.exposure  # Building exposure layer

        # Input checks
        if not hazard_layer.is_point_data:
            message = (
                'Input hazard must be a vector point layer. I got %s '
                'with layer type %s' %
                (hazard_layer.get_name(), hazard_layer.get_geometry_name()))
            raise Exception(message)

        # Make hazard layer by buffering the point
        centers = hazard_layer.get_geometry()
        features = hazard_layer.get_data()
        radii_meter = [x * 1000 for x in radii]  # Convert to meters
        hazard_layer = buffer_points(centers,
                                     radii_meter,
                                     hazard_zone_attribute,
                                     data_table=features)
        # Category names for the impact zone
        category_names = radii_meter
        category_names.append(not_affected_value)

        # Get names of volcanoes considered
        if volcano_name_attribute in hazard_layer.get_attribute_names():
            volcano_name_list = set()
            for row in hazard_layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[volcano_name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)
        else:
            self.volcano_names = tr('Not specified in data')

        # Find the target field name that has no conflict with the attribute
        # names in the hazard layer
        hazard_attribute_names = hazard_layer.get_attribute_names()
        target_field = get_non_conflicting_attribute_name(
            target_field, hazard_attribute_names)

        # Run interpolation function for polygon2polygon
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=None)

        # Extract relevant interpolated layer data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for category in radii_meter:
            self.affected_buildings[category] = {}

        # Iterate the interpolated building layer
        for i in range(len(features)):
            hazard_value = features[i][hazard_zone_attribute]
            if not hazard_value:
                hazard_value = not_affected_value
            features[i][target_field] = hazard_value

            # Count affected buildings by usage type if available
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])

            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][tr(
                    'Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.generate_html_report()

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        colours = colours[::-1]  # flip
        colours = colours[:len(category_names)]
        style_classes = []

        i = 0
        for category_name in category_names:
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(category_names):
                i = len(category_names) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic buffered point')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(building)')
        legend_title = tr('Building count')

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic buffered point'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': target_field,
                'map_title': map_title,
                'legend_notes': legend_notes,
                'legend_units': legend_units,
                'legend_title': legend_title
            },
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #18
0
    def run(self):
        """Classified hazard impact to buildings (e.g. from Open Street Map).
        """
        self.validate()
        self.prepare()

        self.provenance.append_step(
            'Calculating Step', 'Impact function is calculating the impact.')

        # Value from layer's keywords
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None

        # The 3 classes
        categorical_hazards = self.parameters['Categorical hazards'].value
        low_t = categorical_hazards[0].value
        medium_t = categorical_hazards[1].value
        high_t = categorical_hazards[2].value

        # Determine attribute name for hazard levels
        if self.hazard.layer.is_raster:
            hazard_attribute = 'level'
        else:
            hazard_attribute = None

        interpolated_result = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute,
            mode='constant')

        # Extract relevant exposure data
        attribute_names = interpolated_result.get_attribute_names()
        attributes = interpolated_result.get_data()

        buildings_total = len(interpolated_result)
        # Calculate building impact
        self.buildings = {}
        self.affected_buildings = OrderedDict([(tr('High Hazard Class'), {}),
                                               (tr('Medium Hazard Class'), {}),
                                               (tr('Low Hazard Class'), {})])
        for i in range(buildings_total):

            if (structure_class_field
                    and structure_class_field in attribute_names):
                usage = attributes[i][structure_class_field]
            else:
                usage = get_osm_building_usage(attribute_names, attributes[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)
                    ])

            # Count all buildings by type
            self.buildings[usage] += 1
            attributes[i][self.target_field] = 0
            attributes[i][self.affected_field] = 0
            level = float(attributes[i]['level'])
            level = float(numpy_round(level))
            if level == high_t:
                impact_level = tr('High Hazard Class')
            elif level == medium_t:
                impact_level = tr('Medium Hazard Class')
            elif level == low_t:
                impact_level = tr('Low Hazard Class')
            else:
                continue

            # Add calculated impact to existing attributes
            attributes[i][self.target_field] = {
                tr('High Hazard Class'): 3,
                tr('Medium Hazard Class'): 2,
                tr('Low Hazard Class'): 1
            }[impact_level]
            attributes[i][self.affected_field] = 1
            # Count affected buildings by type
            self.affected_buildings[impact_level][usage][tr(
                'Buildings Affected')] += 1

        # Consolidate the small building usage groups < 25 to other
        # Building threshold #2468
        postprocessors = self.parameters['postprocessors']
        building_postprocessors = postprocessors['BuildingType'][0]
        self.building_report_threshold = building_postprocessors.value[0].value
        self._consolidate_to_other()

        # Create style
        style_classes = [
            dict(label=tr('High'),
                 value=3,
                 colour='#F31A1C',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Medium'),
                 value=2,
                 colour='#F4A442',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Low'),
                 value=1,
                 colour='#EBF442',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2),
            dict(label=tr('Not Affected'),
                 value=None,
                 colour='#1EFC7C',
                 transparency=0,
                 size=2,
                 border_color='#969696',
                 border_width=0.2)
        ]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        impact_table = impact_summary = self.html_report()

        # For printing map purpose
        map_title = tr('Buildings affected')
        legend_title = tr('Structure inundated status')
        legend_units = tr('(Low, Medium, High)')

        extra_keywords = {
            'impact_summary': impact_summary,
            'impact_table': impact_table,
            'target_field': self.affected_field,
            'map_title': map_title,
            'legend_units': legend_units,
            'legend_title': legend_title,
            'buildings_total': buildings_total,
            'buildings_affected': self.total_affected_buildings
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        vector_layer = Vector(data=attributes,
                              projection=self.exposure.layer.get_projection(),
                              geometry=self.exposure.layer.get_geometry(),
                              name=tr('Estimated buildings affected'),
                              keywords=impact_layer_keywords,
                              style_info=style_info)
        self._impact = vector_layer
        return vector_layer
Example #19
0
    def run(self):
        """Risk plugin for volcano hazard on building/structure.

        Counts number of building exposed to each volcano hazard zones.

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        # Get parameters from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        name_attribute = self.hazard.keyword('volcano_name_field')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        # Input checks
        if not self.hazard.layer.is_polygon_data:
            message = (
                'Input hazard must be a polygon. I got %s with '
                'layer type %s' %
                (self.hazard.name, self.hazard.layer.get_geometry_name()))
            raise Exception(message)

        # Check if hazard_zone_attribute exists in hazard_layer
        if (self.hazard_class_attribute not in
                self.hazard.layer.get_attribute_names()):
            message = (
                'Hazard data %s did not contain expected attribute %s ' %
                (self.hazard.name, self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Get names of volcanoes considered
        if name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = set()
            for row in self.hazard.layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)
        else:
            self.volcano_names = tr('Not specified in data')

        # Run interpolation function for polygon2raster
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer, self.exposure.layer)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        # Hazard zone categories from hazard layer
        hazard_zone_categories = list(
            set(self.hazard.layer.get_data(self.hazard_class_attribute)))

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for hazard_category in hazard_zone_categories:
            self.affected_buildings[hazard_category] = {}

        for i in range(len(features)):
            hazard_value = features[i][self.hazard_class_attribute]
            if not hazard_value:
                hazard_value = self._not_affected_value
            features[i][self.target_field] = hazard_value
            if (self.exposure_class_attribute and
                    self.exposure_class_attribute in attribute_names):
                usage = features[i][self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, features[i])
            if usage in [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][
                        usage] = OrderedDict([
                            (tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.generate_html_report()
        category_names = hazard_zone_categories
        category_names.append(self._not_affected_value)

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        colours = colours[::-1]  # flip

        colours = colours[:len(category_names)]

        style_classes = []

        i = 0
        for category_name in category_names:
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(category_names):
                i = len(category_names) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic hazard zone'),
            keywords={'impact_summary': impact_summary,
                      'impact_table': impact_table,
                      'target_field': self.target_field,
                      'map_title': map_title,
                      'legend_notes': legend_notes,
                      'legend_units': legend_units,
                      'legend_title': legend_title},
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
    def run(self, layers):
        """Flood impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard_layer: Hazard layer of flood
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer
        """
        threshold = self.parameters['threshold [m]']  # Flood threshold [m]

        verify(isinstance(threshold, float),
               'Expected thresholds to be a float. Got %s' % str(threshold))

        # Extract data
        hazard_layer = get_hazard_layer(layers)  # Depth
        exposure_layer = get_exposure_layer(layers)  # Building locations

        question = get_question(
            hazard_layer.get_name(),
            exposure_layer.get_name(),
            self)

        # Determine attribute name for hazard levels
        if hazard_layer.is_raster:
            mode = 'grid'
            hazard_attribute = 'depth'
        else:
            mode = 'regions'
            hazard_attribute = None

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)
        buildings = {}

        # The number of affected buildings
        affected_count = 0

        # The variable for grid mode
        inundated_count = 0
        wet_count = 0
        dry_count = 0
        inundated_buildings = {}
        wet_buildings = {}
        dry_buildings = {}

        # The variable for regions mode
        affected_buildings = {}

        if mode == 'grid':
            for i in range(total_features):
                # Get the interpolated depth
                water_depth = float(features[i]['depth'])
                if water_depth <= 0:
                    inundated_status = 0  # dry
                elif water_depth >= threshold:
                    inundated_status = 1  # inundated
                else:
                    inundated_status = 2  # wet

                # Count affected buildings by usage type if available
                usage = get_osm_building_usage(attribute_names, features[i])
                if usage is not None and usage != 0:
                    key = usage
                else:
                    key = 'unknown'

                if key not in buildings:
                    buildings[key] = 0
                    inundated_buildings[key] = 0
                    wet_buildings[key] = 0
                    dry_buildings[key] = 0

                # Count all buildings by type
                buildings[key] += 1
                if inundated_status is 0:
                    # Count dry buildings by type
                    dry_buildings[key] += 1
                    # Count total dry buildings
                    dry_count += 1
                if inundated_status is 1:
                    # Count inundated buildings by type
                    inundated_buildings[key] += 1
                    # Count total dry buildings
                    inundated_count += 1
                if inundated_status is 2:
                    # Count wet buildings by type
                    wet_buildings[key] += 1
                    # Count total wet buildings
                    wet_count += 1
                # Add calculated impact to existing attributes
                features[i][self.target_field] = inundated_status
        elif mode == 'regions':
            for i in range(total_features):
                # Use interpolated polygon attribute
                atts = features[i]

                # FIXME (Ole): Need to agree whether to use one or the
                # other as this can be very confusing!
                # For now look for 'affected' first
                if 'affected' in atts:
                    # E.g. from flood forecast
                    # Assume that building is wet if inside polygon
                    # as flagged by attribute Flooded
                    res = atts['affected']
                    if res is None:
                        inundated_status = False
                    else:
                        inundated_status = bool(res)
                elif 'FLOODPRONE' in atts:
                    res = atts['FLOODPRONE']
                    if res is None:
                        inundated_status = False
                    else:
                        inundated_status = res.lower() == 'yes'
                elif DEFAULT_ATTRIBUTE in atts:
                    # Check the default attribute assigned for points
                    # covered by a polygon
                    res = atts[DEFAULT_ATTRIBUTE]
                    if res is None:
                        inundated_status = False
                    else:
                        inundated_status = res
                else:
                    # there is no flood related attribute
                    message = (
                        'No flood related attribute found in %s. I was '
                        'looking for either "affected", "FLOODPRONE" or '
                        '"inapolygon". The latter should have been '
                        'automatically set by call to '
                        'assign_hazard_values_to_exposure_data(). Sorry I '
                        'can\'t help more.')
                    raise Exception(message)

                # Count affected buildings by usage type if available
                usage = get_osm_building_usage(attribute_names, features[i])
                if usage is not None and usage != 0:
                    key = usage
                else:
                    key = 'unknown'

                if key not in buildings:
                    buildings[key] = 0
                    affected_buildings[key] = 0

                # Count all buildings by type
                buildings[key] += 1
                if inundated_status is True:
                    # Count affected buildings by type
                    affected_buildings[key] += 1
                    # Count total affected buildings
                    affected_count += 1

                # Add calculated impact to existing attributes
                features[i][self.target_field] = int(inundated_status)
        else:
            message = (tr('Unknown hazard type %s. Must be either "depth" or '
                          '"grid"') % mode)
            raise Exception(message)

        if mode == 'grid':
            affected_count = inundated_count + wet_count

        # Lump small entries and 'unknown' into 'other' category
        for usage in buildings.keys():
            x = buildings[usage]
            if x < 25 or usage == 'unknown':
                if 'other' not in buildings:
                    buildings['other'] = 0
                    if mode == 'grid':
                        inundated_buildings['other'] = 0
                        wet_buildings['other'] = 0
                        dry_buildings['other'] = 0
                    elif mode == 'regions':
                        affected_buildings['other'] = 0

                buildings['other'] += x
                if mode == 'grid':
                    inundated_buildings['other'] += inundated_buildings[usage]
                    wet_buildings['other'] += wet_buildings[usage]
                    dry_buildings['other'] += dry_buildings[usage]
                    del buildings[usage]
                    del inundated_buildings[usage]
                    del wet_buildings[usage]
                    del dry_buildings[usage]
                elif mode == 'regions':
                    affected_buildings['other'] += affected_buildings[usage]
                    del buildings[usage]
                    del affected_buildings[usage]

        # Generate simple impact report
        table_body = []
        if mode == 'grid':
            table_body = [
                question,
                TableRow([tr('Building type'),
                          tr('Number Inundated'),
                          tr('Number of Wet Buildings'),
                          tr('Number of Dry Buildings'),
                          tr('Total')], header=True),
                TableRow(
                    [tr('All'),
                     format_int(inundated_count),
                     format_int(wet_count),
                     format_int(dry_count),
                     format_int(total_features)])]
        elif mode == 'regions':
            table_body = [
                question,
                TableRow([tr('Building type'),
                          tr('Number flooded'),
                          tr('Total')], header=True),
                TableRow([tr('All'),
                          format_int(affected_count),
                          format_int(total_features)])]

        school_closed = 0
        hospital_closed = 0
        # Generate break down by building usage type if available
        list_type_attribute = [
            'TYPE', 'type', 'amenity', 'building_t', 'office',
            'tourism', 'leisure', 'building']
        intersect_type = set(attribute_names) & set(list_type_attribute)
        if len(intersect_type) > 0:
            # Make list of building types
            building_list = []
            for usage in buildings:
                building_type = usage.replace('_', ' ')

                # Lookup internationalised value if available
                building_type = tr(building_type)
                if mode == 'grid':
                    building_list.append([
                        building_type.capitalize(),
                        format_int(inundated_buildings[usage]),
                        format_int(wet_buildings[usage]),
                        format_int(dry_buildings[usage]),
                        format_int(buildings[usage])])
                elif mode == 'regions':
                    building_list.append([
                        building_type.capitalize(),
                        format_int(affected_buildings[usage]),
                        format_int(buildings[usage])])

                if usage.lower() == 'school':
                    school_closed = 0
                    if mode == 'grid':
                        school_closed += inundated_buildings[usage]
                        school_closed += wet_buildings[usage]
                    elif mode == 'regions':
                        school_closed = affected_buildings[usage]
                if usage.lower() == 'hospital':
                    hospital_closed = 0
                    if mode == 'grid':
                        hospital_closed += inundated_buildings[usage]
                        hospital_closed += wet_buildings[usage]
                    elif mode == 'regions':
                        hospital_closed = affected_buildings[usage]

            # Sort alphabetically
            building_list.sort()

            table_body.append(TableRow(tr('Breakdown by building type'),
                                       header=True))
            for row in building_list:
                s = TableRow(row)
                table_body.append(s)

        # Action Checklist Section
        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(TableRow(
            tr('Are the critical facilities still open?')))
        table_body.append(TableRow(
            tr('Which structures have warning capacity (eg. sirens, speakers, '
               'etc.)?')))
        table_body.append(TableRow(
            tr('Which buildings will be evacuation centres?')))
        table_body.append(TableRow(
            tr('Where will we locate the operations centre?')))
        table_body.append(TableRow(
            tr('Where will we locate warehouse and/or distribution centres?')))

        if school_closed > 0:
            table_body.append(TableRow(
                tr('Where will the students from the %s closed schools go to '
                   'study?') % format_int(school_closed)))

        if hospital_closed > 0:
            table_body.append(TableRow(
                tr('Where will the patients from the %s closed hospitals go '
                   'for treatment and how will we transport them?') %
                format_int(hospital_closed)))

        # Notes Section
        table_body.append(TableRow(tr('Notes'), header=True))
        if mode == 'grid':
            table_body.append(TableRow(
                tr('Buildings are said to be inundated when flood levels '
                   'exceed %.1f m') % threshold))
            table_body.append(TableRow(
                tr('Buildings are said to be wet when flood levels '
                   'are greater than 0 m but less than %.1f m') % threshold))
            table_body.append(TableRow(
                tr('Buildings are said to be dry when flood levels '
                   'are less than 0 m')))
            table_body.append(TableRow(
                tr('Buildings are said to be closed if they are inundated or '
                   'wet')))
            table_body.append(TableRow(
                tr('Buildings are said to be open if they are dry')))
        else:
            table_body.append(TableRow(
                tr('Buildings are said to be flooded when in regions marked '
                   'as affected')))

        # Result
        impact_summary = Table(table_body).toNewlineFreeString()
        impact_table = impact_summary

        # Prepare impact layer
        map_title = tr('Buildings inundated')
        legend_title = tr('Structure inundated status')
        legend_units = ''
        style_classes = []

        if mode == 'grid':
            style_classes = [
                dict(
                    label=tr('Dry (<= 0 m)'),
                    value=0,
                    colour='#1EFC7C',
                    transparency=0,
                    size=1
                ),
                dict(
                    label=tr('Wet (0 m - %.1f m)') % threshold,
                    value=2,
                    colour='#FF9900',
                    transparency=0,
                    size=1
                ),
                dict(
                    label=tr('Inundated (>= %.1f m)') % threshold,
                    value=1,
                    colour='#F31A1C',
                    transparency=0,
                    size=1
                )]
            legend_units = tr('(inundated, wet, or dry)')
        elif mode == 'regions':
            style_classes = [
                dict(
                    label=tr('Not Inundated'),
                    value=0,
                    colour='#1EFC7C',
                    transparency=0,
                    size=1),
                dict(
                    label=tr('Inundated'),
                    value=1,
                    colour='#F31A1C',
                    ztransparency=0, size=1)]
            legend_units = tr('(inundated or not inundated)')

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # Create vector layer and return
        vector_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': self.target_field,
                'map_title': map_title,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'buildings_total': total_features,
                'buildings_affected': affected_count},
            style_info=style_info)
        return vector_layer
Example #21
0
    def run(self):
        """Risk plugin for classified polygon hazard on building/structure.

        Counts number of building exposed to each hazard zones.

        :returns: Map of building exposed to each hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare()

        # Value from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            self.exposure_class_attribute = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            self.exposure_class_attribute = None

        hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex(
            self.hazard_class_attribute)

        # Check if hazard_zone_attribute exists in hazard_layer
        if hazard_zone_attribute_index < 0:
            message = (
                'Hazard data %s does not contain expected attribute %s ' %
                (self.hazard.layer.name(), self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # Hazard zone categories from hazard layer
        self.hazard_zones = self.hazard.layer.uniqueValues(
            hazard_zone_attribute_index)

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for hazard_zone in self.hazard_zones:
            self.affected_buildings[hazard_zone] = {}

        wgs84_extent = QgsRectangle(
            self.requested_extent[0], self.requested_extent[1],
            self.requested_extent[2], self.requested_extent[3])

        # Run interpolation function for polygon2polygon
        interpolated_layer = interpolate_polygon_polygon(
            self.hazard.layer, self.exposure.layer, wgs84_extent)

        new_field = QgsField(self.target_field, QVariant.String)
        interpolated_layer.dataProvider().addAttributes([new_field])
        interpolated_layer.updateFields()

        attribute_names = [
            field.name() for field in interpolated_layer.pendingFields()]
        target_field_index = interpolated_layer.fieldNameIndex(
            self.target_field)
        changed_values = {}

        if interpolated_layer.featureCount() < 1:
            raise ZeroImpactException()

        # Extract relevant interpolated data
        for feature in interpolated_layer.getFeatures():
            hazard_value = feature[self.hazard_class_attribute]
            if not hazard_value:
                hazard_value = self._not_affected_value
            changed_values[feature.id()] = {target_field_index: hazard_value}

            if (self.exposure_class_attribute and
                    self.exposure_class_attribute in attribute_names):
                usage = feature[self.exposure_class_attribute]
            else:
                usage = get_osm_building_usage(attribute_names, feature)

            if usage is None:
                usage = tr('Unknown')
            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict(
                        [(tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        interpolated_layer.dataProvider().changeAttributeValues(changed_values)

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.html_report()

        # Create style
        categories = self.hazard_zones
        categories.append(self._not_affected_value)
        colours = color_ramp(len(categories))
        style_classes = []

        i = 0
        for hazard_zone in self.hazard_zones:
            style_class = dict()
            style_class['label'] = tr(hazard_zone)
            style_class['transparency'] = 0
            style_class['value'] = hazard_zone
            style_class['size'] = 1
            style_class['colour'] = colours[i]
            style_classes.append(style_class)
            i += 1

        # Override style info with new classes and name
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by each hazard zone')
        legend_title = tr('Building count')
        legend_units = tr('(building)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        # Create vector layer and return
        impact_layer = Vector(
            data=interpolated_layer,
            name=tr('Buildings affected by each hazard zone'),
            keywords={'impact_summary': impact_summary,
                      'impact_table': impact_table,
                      'target_field': self.target_field,
                      'map_title': map_title,
                      'legend_notes': legend_notes,
                      'legend_units': legend_units,
                      'legend_title': legend_title},
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Example #22
0
    def run(self, layers=None):
        """Earthquake impact to buildings (e.g. from OpenStreetMap).

        :param layers: All the input layers (Hazard Layer and Exposure Layer)
        """
        self.validate()
        self.prepare(layers)

        LOGGER.debug("Running earthquake building impact")

        # merely initialize
        building_value = 0
        contents_value = 0

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

        # Class Attribute and Label.

        class_1 = {"label": tr("Low"), "class": 1}
        class_2 = {"label": tr("Medium"), "class": 2}
        class_3 = {"label": tr("High"), "class": 3}

        # Extract data
        hazard_layer = self.hazard  # Depth
        exposure_layer = self.exposure  # Building locations

        # Define attribute name for hazard levels.
        hazard_attribute = "mmi"

        # Determine if exposure data have NEXIS attributes.
        attribute_names = exposure_layer.get_attribute_names()
        if "FLOOR_AREA" in attribute_names and "BUILDING_C" in attribute_names and "CONTENTS_C" in attribute_names:
            self.is_nexis = True
        else:
            self.is_nexis = False

        # Interpolate hazard level to building locations.
        interpolate_result = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=hazard_attribute
        )

        # Extract relevant exposure data
        # attribute_names = interpolate_result.get_attribute_names()
        attributes = interpolate_result.get_data()

        interpolate_size = len(interpolate_result)

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([(tr("High"), {}), (tr("Medium"), {}), (tr("Low"), {})])
        for i in range(interpolate_size):
            # Classify building according to shake level
            # and calculate dollar losses

            if self.is_nexis:
                try:
                    area = float(attributes[i]["FLOOR_AREA"])
                except (ValueError, KeyError):
                    # print 'Got area', attributes[i]['FLOOR_AREA']
                    area = 0.0

                try:
                    building_value_density = float(attributes[i]["BUILDING_C"])
                except (ValueError, KeyError):
                    # print 'Got bld value', attributes[i]['BUILDING_C']
                    building_value_density = 0.0

                try:
                    contents_value_density = float(attributes[i]["CONTENTS_C"])
                except (ValueError, KeyError):
                    # print 'Got cont value', attributes[i]['CONTENTS_C']
                    contents_value_density = 0.0

                building_value = building_value_density * area
                contents_value = contents_value_density * area

            usage = get_osm_building_usage(attribute_names, attributes[i])
            if usage is None or usage == 0:
                usage = "unknown"

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    if self.is_nexis:
                        self.affected_buildings[category][usage] = OrderedDict(
                            [
                                (tr("Buildings Affected"), 0),
                                (tr("Buildings value ($M)"), 0),
                                (tr("Contents value ($M)"), 0),
                            ]
                        )
                    else:
                        self.affected_buildings[category][usage] = OrderedDict([(tr("Buildings Affected"), 0)])
            self.buildings[usage] += 1
            try:
                mmi = float(attributes[i][hazard_attribute])  # MMI
            except TypeError:
                mmi = 0.0
            if t0 <= mmi < t1:
                cls = 1
                category = tr("Low")
            elif t1 <= mmi < t2:
                cls = 2
                category = tr("Medium")
            elif t2 <= mmi:
                cls = 3
                category = tr("High")
            else:
                # Not reported for less than level t0
                continue
            attributes[i][self.target_field] = cls
            self.affected_buildings[category][usage][tr("Buildings Affected")] += 1
            if self.is_nexis:
                self.affected_buildings[category][usage][tr("Buildings value ($M)")] += building_value / 1000000.0
                self.affected_buildings[category][usage][tr("Contents value ($M)")] += contents_value / 1000000.0

        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        impact_table = impact_summary = self.generate_html_report()

        # Create style
        style_classes = [
            dict(label=class_1["label"], value=class_1["class"], colour="#ffff00", transparency=1),
            dict(label=class_2["label"], value=class_2["class"], colour="#ffaa00", transparency=1),
            dict(label=class_3["label"], value=class_3["class"], colour="#ff0000", transparency=1),
        ]
        style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type="categorizedSymbol")

        # For printing map purpose
        map_title = tr("Building affected by earthquake")
        legend_notes = tr("The level of the impact is according to the " "threshold the user input.")
        legend_units = tr("(mmi)")
        legend_title = tr("Impact level")

        # Create vector layer and return
        result_layer = Vector(
            data=attributes,
            projection=interpolate_result.get_projection(),
            geometry=interpolate_result.get_geometry(),
            name=tr("Estimated buildings affected"),
            keywords={
                "impact_summary": impact_summary,
                "impact_table": impact_table,
                "map_title": map_title,
                "legend_notes": legend_notes,
                "legend_units": legend_units,
                "legend_title": legend_title,
                "target_field": self.target_field,
                "statistics_type": self.statistics_type,
                "statistics_classes": self.statistics_classes,
            },
            style_info=style_info,
        )

        msg = "Created vector layer %s" % str(result_layer)
        LOGGER.debug(msg)
        self._impact = result_layer
        return result_layer
Example #23
0
    def run(self):
        """Flood impact to buildings (e.g. from Open Street Map)."""
        self.validate()
        self.prepare()

        threshold = self.parameters['threshold'].value  # Flood threshold [m]

        verify(isinstance(threshold, float),
               'Expected thresholds to be a float. Got %s' % str(threshold))

        # Determine attribute name for hazard levels
        hazard_attribute = 'depth'

        # Interpolate hazard level to building locations
        interpolated_layer = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute)

        # Extract relevant exposure data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()
        total_features = len(interpolated_layer)

        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (tr('Number Inundated'), {}),
            (tr('Number of Wet Buildings'), {}),
            (tr('Number of Dry Buildings'), {})
        ])
        for i in range(total_features):
            # Get the interpolated depth
            water_depth = float(features[i]['depth'])
            if water_depth <= 0:
                inundated_status = 0  # dry
            elif water_depth >= threshold:
                inundated_status = 1  # inundated
            else:
                inundated_status = 2  # wet

            # Count affected buildings by usage type if available
            if (structure_class_field in attribute_names and
                    structure_class_field):
                usage = features[i].get(structure_class_field, None)
            else:
                usage = get_osm_building_usage(
                    attribute_names, features[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            # Add calculated impact to existing attributes
            features[i][self.target_field] = inundated_status
            category = [
                tr('Number of Dry Buildings'),
                tr('Number Inundated'),
                tr('Number of Wet Buildings')][inundated_status]
            self.affected_buildings[category][usage][
                tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()
        # Generate simple impact report
        impact_table = impact_summary = self.generate_html_report()

        # For printing map purpose
        map_title = tr('Buildings inundated')
        legend_title = tr('Structure inundated status')
        legend_units = tr('(inundated, wet, or dry)')

        style_classes = [
            dict(
                label=tr('Dry (<= 0 m)'),
                value=0,
                colour='#1EFC7C',
                transparency=0,
                size=1
            ),
            dict(
                label=tr('Wet (0 m - %.1f m)') % threshold,
                value=2,
                colour='#FF9900',
                transparency=0,
                size=1
            ),
            dict(
                label=tr('Inundated (>= %.1f m)') % threshold,
                value=1,
                colour='#F31A1C',
                transparency=0,
                size=1
            )]

        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        vector_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': self.target_field,
                'map_title': map_title,
                'legend_title': legend_title,
                'legend_units': legend_units,
                'buildings_total': total_features,
                'buildings_affected': self.total_affected_buildings},
            style_info=style_info)
        # Create vector layer and return
        self._impact = vector_layer
        return vector_layer
Example #24
0
    def run(self):
        """Earthquake impact to buildings (e.g. from OpenStreetMap)."""
        self.validate()
        self.prepare()

        LOGGER.debug('Running earthquake building impact')

        # merely initialize
        building_value = 0
        contents_value = 0

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

        # Class Attribute and Label.

        class_1 = {'label': tr('Low'), 'class': 1}
        class_2 = {'label': tr('Medium'), 'class': 2}
        class_3 = {'label': tr('High'), 'class': 3}

        # Define attribute name for hazard levels.
        hazard_attribute = 'mmi'

        # Determine if exposure data have NEXIS attributes.
        attribute_names = self.exposure.layer.get_attribute_names()
        if (
                'FLOOR_AREA' in attribute_names and
                'BUILDING_C' in attribute_names and
                'CONTENTS_C' in attribute_names):
            self.is_nexis = True
        else:
            self.is_nexis = False

        # Interpolate hazard level to building locations.
        interpolate_result = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute
        )

        # Extract relevant exposure data
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None
        attributes = interpolate_result.get_data()

        interpolate_size = len(interpolate_result)

        # Building breakdown
        self.buildings = {}
        # Impacted building breakdown
        self.affected_buildings = OrderedDict([
            (tr('High'), {}),
            (tr('Medium'), {}),
            (tr('Low'), {})
        ])
        removed = []
        for i in range(interpolate_size):
            # Classify building according to shake level
            # and calculate dollar losses

            if self.is_nexis:
                try:
                    area = float(attributes[i]['FLOOR_AREA'])
                except (ValueError, KeyError):
                    # print 'Got area', attributes[i]['FLOOR_AREA']
                    area = 0.0

                try:
                    building_value_density = float(attributes[i]['BUILDING_C'])
                except (ValueError, KeyError):
                    # print 'Got bld value', attributes[i]['BUILDING_C']
                    building_value_density = 0.0

                try:
                    contents_value_density = float(attributes[i]['CONTENTS_C'])
                except (ValueError, KeyError):
                    # print 'Got cont value', attributes[i]['CONTENTS_C']
                    contents_value_density = 0.0

                building_value = building_value_density * area
                contents_value = contents_value_density * area

            if (structure_class_field in attribute_names and
                    structure_class_field):
                usage = attributes[i].get(structure_class_field, None)
            else:
                usage = get_osm_building_usage(
                    attribute_names, attributes[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    if self.is_nexis:
                        self.affected_buildings[category][usage] = OrderedDict(
                            [
                                (tr('Buildings Affected'), 0),
                                (tr('Buildings value ($M)'), 0),
                                (tr('Contents value ($M)'), 0)])
                    else:
                        self.affected_buildings[category][usage] = \
                            OrderedDict([(tr('Buildings Affected'), 0)])
            self.buildings[usage] += 1
            try:
                mmi = float(attributes[i][hazard_attribute])  # MMI
            except TypeError:
                mmi = 0.0
            if t0 <= mmi < t1:
                cls = 1
                category = tr('Low')
            elif t1 <= mmi < t2:
                cls = 2
                category = tr('Medium')
            elif t2 <= mmi:
                cls = 3
                category = tr('High')
            else:
                # Not reported for less than level t0
                removed.append(i)
                continue
            attributes[i][self.target_field] = cls
            self.affected_buildings[
                category][usage][tr('Buildings Affected')] += 1
            if self.is_nexis:
                self.affected_buildings[category][usage][
                    tr('Buildings value ($M)')] += building_value / 1000000.0
                self.affected_buildings[category][usage][
                    tr('Contents value ($M)')] += contents_value / 1000000.0

        # remove uncategorized element
        removed.reverse()
        geometry = interpolate_result.get_geometry()
        for i in range(0, len(removed)):
            del attributes[removed[i]]
            del geometry[removed[i]]

        if len(attributes) < 1:
            raise ZeroImpactException()
        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        impact_table = impact_summary = self.html_report()

        # Create style
        style_classes = [dict(label=class_1['label'], value=class_1['class'],
                              colour='#ffff00', transparency=1),
                         dict(label=class_2['label'], value=class_2['class'],
                              colour='#ffaa00', transparency=1),
                         dict(label=class_3['label'], value=class_3['class'],
                              colour='#ff0000', transparency=1)]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Building affected by earthquake')
        legend_notes = tr('The level of the impact is according to the '
                          'threshold the user input.')
        legend_units = tr('(mmi)')
        legend_title = tr('Impact level')

        # Create vector layer and return
        result_layer = Vector(
            data=attributes,
            projection=interpolate_result.get_projection(),
            geometry=geometry,
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'map_title': map_title,
                'legend_notes': legend_notes,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'target_field': self.target_field,
                'statistics_type': self.statistics_type,
                'statistics_classes': self.statistics_classes},
            style_info=style_info)

        msg = 'Created vector layer %s' % str(result_layer)
        LOGGER.debug(msg)
        self._impact = result_layer
        return result_layer
Example #25
0
    def run(self):
        """Classified hazard impact to buildings (e.g. from Open Street Map).
        """
        self.validate()
        self.prepare()

        # Value from layer's keywords
        # Try to get the value from keyword, if not exist, it will not fail,
        # but use the old get_osm_building_usage
        try:
            structure_class_field = self.exposure.keyword(
                'structure_class_field')
        except KeywordNotFoundError:
            structure_class_field = None

        # The 3 classes
        categorical_hazards = self.parameters['Categorical hazards'].value
        low_t = categorical_hazards[0].value
        medium_t = categorical_hazards[1].value
        high_t = categorical_hazards[2].value

        # Determine attribute name for hazard levels
        if self.hazard.layer.is_raster:
            hazard_attribute = 'level'
        else:
            hazard_attribute = None

        interpolated_result = assign_hazard_values_to_exposure_data(
            self.hazard.layer,
            self.exposure.layer,
            attribute_name=hazard_attribute,
            mode='constant')

        # Extract relevant exposure data
        attribute_names = interpolated_result.get_attribute_names()
        attributes = interpolated_result.get_data()

        buildings_total = len(interpolated_result)
        # Calculate building impact
        self.buildings = {}
        self.affected_buildings = OrderedDict([
            (tr('High Hazard Class'), {}),
            (tr('Medium Hazard Class'), {}),
            (tr('Low Hazard Class'), {})
        ])
        for i in range(buildings_total):

            if (structure_class_field and
                    structure_class_field in attribute_names):
                usage = attributes[i][structure_class_field]
            else:
                usage = get_osm_building_usage(attribute_names, attributes[i])

            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            attributes[i][self.target_field] = 0
            attributes[i][self.affected_field] = 0
            level = float(attributes[i]['level'])
            level = float(numpy_round(level))
            if level == high_t:
                impact_level = tr('High Hazard Class')
            elif level == medium_t:
                impact_level = tr('Medium Hazard Class')
            elif level == low_t:
                impact_level = tr('Low Hazard Class')
            else:
                continue

            # Add calculated impact to existing attributes
            attributes[i][self.target_field] = {
                tr('High Hazard Class'): 3,
                tr('Medium Hazard Class'): 2,
                tr('Low Hazard Class'): 1
            }[impact_level]
            attributes[i][self.affected_field] = 1
            # Count affected buildings by type
            self.affected_buildings[impact_level][usage][
                tr('Buildings Affected')] += 1

        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        # Create style
        style_classes = [dict(label=tr('High'),
                              value=3,
                              colour='#F31A1C',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Medium'),
                              value=2,
                              colour='#F4A442',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Low'),
                              value=1,
                              colour='#EBF442',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Not Affected'),
                              value=None,
                              colour='#1EFC7C',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2)]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        impact_table = impact_summary = self.html_report()

        # For printing map purpose
        map_title = tr('Buildings affected')
        legend_title = tr('Structure inundated status')
        legend_units = tr('(Low, Medium, High)')

        # Create vector layer and return
        vector_layer = Vector(
            data=attributes,
            projection=self.exposure.layer.get_projection(),
            geometry=self.exposure.layer.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': self.affected_field,
                'map_title': map_title,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'buildings_total': buildings_total,
                'buildings_affected': self.total_affected_buildings},
            style_info=style_info)
        self._impact = vector_layer
        return vector_layer
Example #26
0
    def run(self, layers=None):
        """Counts number of building exposed to each volcano hazard zones.

        :param layers: List of layers expected to contain.
                * hazard_layer: Hazard layer of volcano
                * exposure_layer: Vector layer of structure data on
                the same grid as hazard_layer

        :returns: Map of building exposed to volcanic hazard zones.
                  Table with number of buildings affected
        :rtype: dict
        """
        self.validate()
        self.prepare(layers)
        # Target Field
        target_field = 'zone'
        # Hazard Zone Attribute
        hazard_zone_attribute = 'radius'
        # Not Affected Value
        not_affected_value = 'Not Affected'

        # Parameters
        radii = self.parameters['distances [km]']
        volcano_name_attribute = self.parameters['volcano name attribute']

        # Identify hazard and exposure layers
        hazard_layer = self.hazard  # Volcano hazard layer
        exposure_layer = self.exposure  # Building exposure layer

        # Input checks
        if not hazard_layer.is_point_data:
            message = (
                'Input hazard must be a vector point layer. I got %s '
                'with layer type %s' % (
                    hazard_layer.get_name(), hazard_layer.get_geometry_name()))
            raise Exception(message)

        # Make hazard layer by buffering the point
        centers = hazard_layer.get_geometry()
        features = hazard_layer.get_data()
        radii_meter = [x * 1000 for x in radii]  # Convert to meters
        hazard_layer = buffer_points(
            centers,
            radii_meter,
            hazard_zone_attribute,
            data_table=features)
        # Category names for the impact zone
        category_names = radii_meter
        category_names.append(not_affected_value)

        # Get names of volcanoes considered
        if volcano_name_attribute in hazard_layer.get_attribute_names():
            volcano_name_list = set()
            for row in hazard_layer.get_data():
                # Run through all polygons and get unique names
                volcano_name_list.add(row[volcano_name_attribute])
            self.volcano_names = ', '.join(volcano_name_list)

        # Find the target field name that has no conflict with the attribute
        # names in the hazard layer
        hazard_attribute_names = hazard_layer.get_attribute_names()
        target_field = get_non_conflicting_attribute_name(
            target_field, hazard_attribute_names)

        # Run interpolation function for polygon2polygon
        interpolated_layer = assign_hazard_values_to_exposure_data(
            hazard_layer, exposure_layer, attribute_name=None)

        # Extract relevant interpolated layer data
        attribute_names = interpolated_layer.get_attribute_names()
        features = interpolated_layer.get_data()

        self.buildings = {}
        self.affected_buildings = OrderedDict()
        for category in radii_meter:
            self.affected_buildings[category] = {}

        # Iterate the interpolated building layer
        for i in range(len(features)):
            hazard_value = features[i][hazard_zone_attribute]
            if not hazard_value:
                hazard_value = not_affected_value
            features[i][target_field] = hazard_value

            # Count affected buildings by usage type if available
            usage = get_osm_building_usage(attribute_names, features[i])
            if usage is [None, 'NULL', 'null', 'Null', 0]:
                usage = tr('Unknown')

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][
                        usage] = OrderedDict([
                            (tr('Buildings Affected'), 0)])

            self.buildings[usage] += 1
            if hazard_value in self.affected_buildings.keys():
                self.affected_buildings[hazard_value][usage][
                    tr('Buildings Affected')] += 1

        # Lump small entries and 'unknown' into 'other' category
        self._consolidate_to_other()

        # Generate simple impact report
        impact_summary = impact_table = self.generate_html_report()

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        colours = colours[::-1]  # flip
        colours = colours[:len(category_names)]
        style_classes = []

        i = 0
        for category_name in category_names:
            style_class = dict()
            style_class['label'] = tr(category_name)
            style_class['transparency'] = 0
            style_class['value'] = category_name
            style_class['size'] = 1

            if i >= len(category_names):
                i = len(category_names) - 1
            style_class['colour'] = colours[i]
            i += 1

            style_classes.append(style_class)

        # Override style info with new classes and name
        style_info = dict(target_field=target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        # For printing map purpose
        map_title = tr('Buildings affected by volcanic buffered point')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(building)')
        legend_title = tr('Building count')

        # Create vector layer and return
        impact_layer = Vector(
            data=features,
            projection=interpolated_layer.get_projection(),
            geometry=interpolated_layer.get_geometry(),
            name=tr('Buildings affected by volcanic buffered point'),
            keywords={'impact_summary': impact_summary,
                      'impact_table': impact_table,
                      'target_field': target_field,
                      'map_title': map_title,
                      'legend_notes': legend_notes,
                      'legend_units': legend_units,
                      'legend_title': legend_title},
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
    def run(self, layers=None):
        """Classified hazard impact to buildings (e.g. from Open Street Map).

         :param layers: List of layers expected to contain.
                * hazard: Classified Hazard layer
                * exposure: Vector layer of structure data on
                the same grid as hazard
        """
        self.validate()
        self.prepare(layers)

        # The 3 classes
        low_t = self.parameters['low_hazard_class']
        medium_t = self.parameters['medium_hazard_class']
        high_t = self.parameters['high_hazard_class']

        # Extract data
        hazard = self.hazard      # Classified Hazard
        exposure = self.exposure  # Building locations

        # Determine attribute name for hazard levels
        if hazard.is_raster:
            hazard_attribute = 'level'
        else:
            hazard_attribute = None

        interpolated_result = assign_hazard_values_to_exposure_data(
            hazard,
            exposure,
            attribute_name=hazard_attribute,
            mode='constant')

        # Extract relevant exposure data
        attribute_names = interpolated_result.get_attribute_names()
        attributes = interpolated_result.get_data()

        buildings_total = len(interpolated_result)
        # Calculate building impact
        self.buildings = {}
        self.affected_buildings = OrderedDict([
            (tr('High Hazard Class'), {}),
            (tr('Medium Hazard Class'), {}),
            (tr('Low Hazard Class'), {})
        ])
        for i in range(buildings_total):
            usage = get_osm_building_usage(attribute_names, attributes[i])
            if usage is None or usage == 0:
                usage = 'unknown'

            if usage not in self.buildings:
                self.buildings[usage] = 0
                for category in self.affected_buildings.keys():
                    self.affected_buildings[category][usage] = OrderedDict([
                        (tr('Buildings Affected'), 0)])

            # Count all buildings by type
            self.buildings[usage] += 1
            attributes[i][self.target_field] = 0
            attributes[i][self.affected_field] = 0
            level = float(attributes[i]['level'])
            level = float(numpy_round(level))
            if level == high_t:
                impact_level = tr('High Hazard Class')
            elif level == medium_t:
                impact_level = tr('Medium Hazard Class')
            elif level == low_t:
                impact_level = tr('Low Hazard Class')
            else:
                continue

            # Add calculated impact to existing attributes
            attributes[i][self.target_field] = {
                tr('High Hazard Class'): 3,
                tr('Medium Hazard Class'): 2,
                tr('Low Hazard Class'): 1
            }[impact_level]
            attributes[i][self.affected_field] = 1
            # Count affected buildings by type
            self.affected_buildings[impact_level][usage][
                tr('Buildings Affected')] += 1

        # Consolidate the small building usage groups < 25 to other
        self._consolidate_to_other()

        # Create style
        style_classes = [dict(label=tr('High'),
                              value=3,
                              colour='#F31A1C',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Medium'),
                              value=2,
                              colour='#F4A442',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Low'),
                              value=1,
                              colour='#EBF442',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2),
                         dict(label=tr('Not Affected'),
                              value=None,
                              colour='#1EFC7C',
                              transparency=0,
                              size=2,
                              border_color='#969696',
                              border_width=0.2)]
        style_info = dict(target_field=self.target_field,
                          style_classes=style_classes,
                          style_type='categorizedSymbol')

        impact_table = impact_summary = self.generate_html_report()
        # For printing map purpose
        map_title = tr('Buildings affected')
        legend_units = tr('(Low, Medium, High)')
        legend_title = tr('Structure inundated status')

        # Create vector layer and return
        vector_layer = Vector(
            data=attributes,
            projection=exposure.get_projection(),
            geometry=exposure.get_geometry(),
            name=tr('Estimated buildings affected'),
            keywords={
                'impact_summary': impact_summary,
                'impact_table': impact_table,
                'target_field': self.affected_field,
                'map_title': map_title,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'buildings_total': buildings_total,
                'buildings_affected': self.total_affected_buildings},
            style_info=style_info)
        self._impact = vector_layer
        return vector_layer