Source code for safe.impact_functions.generic.classified_raster_population.impact_function

# coding=utf-8
"""
InaSAFE Disaster risk assessment tool by AusAid - ** Generic Impact
Function on Population for Classified Hazard.**

Contact : [email protected]

.. note:: This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

.. todo:: Check raster is single band

"""

__author__ = 'lucernae'
__date__ = '24/03/15'
__revision__ = '$Format:%H$'
__copyright__ = ('Copyright 2014, Australia Indonesia Facility for '
                 'Disaster Reduction')

import numpy
import itertools

from safe.impact_functions.bases.classified_rh_continuous_re import \
    ClassifiedRHContinuousRE
from safe.impact_functions.core import (
    population_rounding,
    has_no_data)
from safe.storage.raster import Raster
from safe.common.utilities import (
    humanize_class,
    create_classes,
    create_label,
    get_thousand_separator)
from safe.utilities.i18n import tr
from safe.impact_functions.core import no_population_impact_message
from safe.impact_functions.generic.\
    classified_raster_population.metadata_definitions import \
    ClassifiedRasterHazardPopulationMetadata
from safe.impact_functions.impact_function_manager\
    import ImpactFunctionManager
from safe.gui.tools.minimum_needs.needs_profile import add_needs_parameters
from safe.common.exceptions import (
    FunctionParametersError, ZeroImpactException)
from safe.impact_reports.population_exposure_report_mixin import \
    PopulationExposureReportMixin
import safe.messaging as m
from safe.messaging import styles


[docs]class ClassifiedRasterHazardPopulationFunction( ClassifiedRHContinuousRE, PopulationExposureReportMixin): # noinspection PyUnresolvedReferences """Plugin for impact of population as derived by classified hazard.""" _metadata = ClassifiedRasterHazardPopulationMetadata() def __init__(self): super(ClassifiedRasterHazardPopulationFunction, self).__init__() self.impact_function_manager = ImpactFunctionManager() # AG: Use the proper minimum needs, update the parameters self.parameters = add_needs_parameters(self.parameters) self.no_data_warning = False
[docs] def notes(self): """Return the notes section of the report. :return: The notes that should be attached to this impact report. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') message.add( m.Heading(tr('Notes and assumptions'), **styles.INFO_STYLE)) checklist = m.BulletedList() checklist.add(tr( 'Total population in the analysis area: %s' ) % population_rounding(self.total_population)) checklist.add(tr( '<sup>1</sup>People need evacuation if they are in a ' 'hazard zone.')) if self.no_data_warning: checklist.add(tr( 'The layers contained "no data" values. This missing data ' 'was carried through to the impact layer.')) checklist.add(tr( '"No data" values in the impact layer were treated as 0 ' 'when counting the affected or total population.')) checklist.add(tr( 'All values are rounded up to the nearest integer in ' 'order to avoid representing human lives as fractions.')) checklist.add(tr( 'Population rounding is applied to all population ' 'values, which may cause discrepancies when adding value.')) message.add(checklist) return message
[docs] def run(self): """Plugin for impact of population as derived by classified hazard. Counts number of people exposed to each class of the hazard Return Map of population exposed to high class Table with number of people in each class """ self.validate() self.prepare() # The 3 classes # TODO (3.2): shouldnt these be defined in keywords rather? TS categorical_hazards = self.parameters['Categorical hazards'].value low_class = categorical_hazards[0].value medium_class = categorical_hazards[1].value high_class = categorical_hazards[2].value # The classes must be different to each other unique_classes_flag = all( x != y for x, y in list( itertools.combinations( [low_class, medium_class, high_class], 2))) if not unique_classes_flag: raise FunctionParametersError( 'There is hazard class that has the same value with other ' 'class. Please check the parameters.') # Extract data as numeric arrays hazard_data = self.hazard.layer.get_data(nan=True) # Class if has_no_data(hazard_data): self.no_data_warning = True # Calculate impact as population exposed to each class population = self.exposure.layer.get_data(scaling=True) # Get all population data that falls in each hazard class high_hazard_population = numpy.where( hazard_data == high_class, population, 0) medium_hazard_population = numpy.where( hazard_data == medium_class, population, 0) low_hazard_population = numpy.where( hazard_data == low_class, population, 0) affected_population = ( high_hazard_population + medium_hazard_population + low_hazard_population) # Carry the no data values forward to the impact layer. affected_population = numpy.where( numpy.isnan(population), numpy.nan, affected_population) affected_population = numpy.where( numpy.isnan(hazard_data), numpy.nan, affected_population) # Count totals self.total_population = int(numpy.nansum(population)) self.affected_population[ tr('Population in High hazard class areas')] = int( numpy.nansum(high_hazard_population)) self.affected_population[ tr('Population in Medium hazard class areas')] = int( numpy.nansum(medium_hazard_population)) self.affected_population[ tr('Population in Low hazard class areas')] = int( numpy.nansum(low_hazard_population)) self.unaffected_population = ( self.total_population - self.total_affected_population) # check for zero impact if self.total_affected_population == 0: message = no_population_impact_message(self.question) raise ZeroImpactException(message) self.minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] total_needs = self.total_needs impact_table = impact_summary = self.html_report() # Create style colours = [ '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] classes = create_classes(affected_population.flat[:], len(colours)) interval_classes = humanize_class(classes) style_classes = [] for i in xrange(len(colours)): style_class = dict() if i == 1: label = create_label( interval_classes[i], tr('Low Population [%i people/cell]' % classes[i])) elif i == 4: label = create_label( interval_classes[i], tr('Medium Population [%i people/cell]' % classes[i])) elif i == 7: label = create_label( interval_classes[i], tr('High Population [%i people/cell]' % classes[i])) else: label = create_label(interval_classes[i]) style_class['label'] = label style_class['quantity'] = classes[i] if i == 0: transparency = 100 else: transparency = 0 style_class['transparency'] = transparency style_class['colour'] = colours[i] style_classes.append(style_class) style_info = dict( target_field=None, style_classes=style_classes, style_type='rasterStyle') # For printing map purpose map_title = tr('Number of people affected in each class') legend_title = tr('Number of People') legend_units = tr('(people per cell)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) # Create raster object and return raster_layer = Raster( data=affected_population, projection=self.exposure.layer.get_projection(), geotransform=self.exposure.layer.get_geotransform(), name=tr('People that might %s') % ( self.impact_function_manager .get_function_title(self).lower()), 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, 'total_needs': total_needs}, style_info=style_info) self._impact = raster_layer return raster_layer