Exemplo n.º 1
0
 def test_qgis_raster_layer_loading(self):
     """Test that reading from QgsRasterLayer works."""
     # This line is the cause of the problem:
     qgis_layer = QgsRasterLayer(RASTER_BASE + '.tif', 'test')
     layer = Raster(data=qgis_layer)
     qgis_extent = qgis_layer.dataProvider().extent()
     qgis_extent = [qgis_extent.xMinimum(), qgis_extent.yMinimum(),
                    qgis_extent.xMaximum(), qgis_extent.yMaximum()]
     layer_exent = layer.get_bounding_box()
     self.assertListEqual(
         layer_exent, qgis_extent,
         'Expected %s extent, got %s' % (qgis_extent, layer_exent))
Exemplo n.º 2
0
    def test_convert_to_qgis_raster_layer(self):
        """Test that converting to QgsVectorLayer works."""
        # Create vector layer
        layer = Raster(data=RASTER_BASE + '.tif')

        # Convert to QgsRasterLayer
        qgis_layer = layer.as_qgis_native()
        qgis_extent = qgis_layer.dataProvider().extent()
        qgis_extent = [qgis_extent.xMinimum(), qgis_extent.yMinimum(),
                       qgis_extent.xMaximum(), qgis_extent.yMaximum()]
        layer_exent = layer.get_bounding_box()
        self.assertListEqual(layer_exent, qgis_extent)
Exemplo n.º 3
0
 def test_qgis_raster_layer_loading(self):
     """Test that reading from QgsRasterLayer works."""
     # This line is the cause of the problem:
     qgis_layer = QgsRasterLayer(RASTER_BASE + '.tif', 'test')
     layer = Raster(data=qgis_layer)
     qgis_extent = qgis_layer.dataProvider().extent()
     qgis_extent = [qgis_extent.xMinimum(), qgis_extent.yMinimum(),
                    qgis_extent.xMaximum(), qgis_extent.yMaximum()]
     layer_exent = layer.get_bounding_box()
     self.assertListEqual(
         layer_exent, qgis_extent,
         'Expected %s extent, got %s' % (qgis_extent, layer_exent))
Exemplo n.º 4
0
    def test_convert_to_qgis_raster_layer(self):
        """Test that converting to QgsVectorLayer works."""
        # Create vector layer
        keywords = read_keywords(RASTER_BASE + '.keywords')
        layer = Raster(data=RASTER_BASE + '.tif', keywords=keywords)

        # Convert to QgsRasterLayer
        qgis_layer = layer.as_qgis_native()
        qgis_extent = qgis_layer.dataProvider().extent()
        qgis_extent = [qgis_extent.xMinimum(), qgis_extent.yMinimum(),
                       qgis_extent.xMaximum(), qgis_extent.yMaximum()]
        layer_exent = layer.get_bounding_box()
        self.assertListEqual(
            layer_exent, qgis_extent,
            'Expected %s extent, got %s' % (qgis_extent, layer_exent))
Exemplo n.º 5
0
    def run(layers):
        """Risk plugin for earthquake fatalities

        Input
          layers: List of layers expected to contain
              H: Raster layer of flood depth
              P: Raster layer of population data on the same grid as H
        """

        threshold = 1  # Load above which people are regarded affected [kg/m2]

        # Identify hazard and exposure layers
        inundation = get_hazard_layer(layers)    # Tephra load [kg/m2]
        population = get_exposure_layer(layers)  # Density [people/km^2]

        # Extract data as numeric arrays
        D = inundation.get_data(nan=0.0)  # Depth
        P = population.get_data(nan=0.0, scaling=True)  # Population density

        # Calculate impact as population exposed to depths > threshold
        I = numpy.where(D > threshold, P, 0)

        # Generate text with result for this study
        number_of_people_affected = numpy.nansum(I.flat)
        impact_summary = ('%i people affected by ash levels greater '
                   'than %i kg/m^2' % (number_of_people_affected,
                                       threshold))

        # Create raster object and return
        R = Raster(I,
                   projection=inundation.get_projection(),
                   geotransform=inundation.get_geotransform(),
                   name='People affected',
                   keywords={'impact_summary': impact_summary})
        return R
Exemplo n.º 6
0
    def run(layers):
        """Risk plugin for volcano population impact

        Input
          layers: List of layers expected to contain
              H: Raster layer of volcanic hazard level
              P: Raster layer of population data on the same grid as H
        """

        # Identify hazard and exposure layers
        # Volcanic hazard level [0-1]
        volcanic_hazard_level = get_hazard_layer(layers)
        population = get_exposure_layer(layers)  # Density [people/area]

        # Extract data as numeric arrays
        V = volcanic_hazard_level.get_data(nan=0.0)
        # Population density
        P = population.get_data(nan=0.0, scaling=True)

        # Calculate impact as population exposed to depths > threshold
        I = numpy.where(V > 2.0 / 3, P, 0)

        # Generate text with result for this study
        number_of_people_affected = numpy.nansum(I.flat)
        impact_summary = ('%i people affected by volcanic hazard level greater'
                          ' than 0.667' % number_of_people_affected)

        # Create raster object and return
        R = Raster(I,
                   projection=volcanic_hazard_level.get_projection(),
                   geotransform=volcanic_hazard_level.get_geotransform(),
                   name='People affected',
                   keywords={'impact_summary': impact_summary})
        return R
Exemplo n.º 7
0
    def _aggregate_raster_impact(self):
        """Check aggregation on raster impact.

        Created from loadStandardLayers.qgs with:
        - a flood in Jakarta like in 2007
        - Penduduk Jakarta
        - need evacuation
        - kabupaten_jakarta_singlepart.shp

        """
        impact_layer = Raster(data=os.path.join(
            TESTDATA, 'aggregation_test_impact_raster.tif'),
                              name='test raster impact')

        expected_results = [
            ['JAKARTA BARAT', '50540', '12015061.8769531', '237.733713433976'],
            ['JAKARTA PUSAT', '19492', '2943702.11401367', '151.021040119725'],
            [
                'JAKARTA SELATAN', '57367', '1645498.26947021',
                '28.6837078716024'
            ],
            ['JAKARTA UTARA', '55004', '11332095.7334595', '206.023120745027'],
            ['JAKARTA TIMUR', '73949', '10943934.3182373', '147.992999475819']
        ]

        self._aggregate(impact_layer, expected_results)
Exemplo n.º 8
0
    def test_convert_to_qgis_raster_layer(self):
        """Test that converting to QgsVectorLayer works."""
        if qgis_imported:
            # Create vector layer
            keywords = read_keywords(RASTER_BASE + '.keywords')
            layer = Raster(data=RASTER_BASE + '.tif', keywords=keywords)

            # Convert to QgsRasterLayer
            qgis_layer = layer.as_qgis_native()
            qgis_extent = qgis_layer.dataProvider().extent()
            qgis_extent = [qgis_extent.xMinimum(), qgis_extent.yMinimum(),
                           qgis_extent.xMaximum(), qgis_extent.yMaximum()]
            layer_exent = layer.get_bounding_box()
            self.assertListEqual(
                layer_exent, qgis_extent,
                'Expected %s extent, got %s' % (qgis_extent, layer_exent))
Exemplo n.º 9
0
def _flood_severity(hazard_files):
    """
    Accumulate the hazard level
    """
    # Value above which people are regarded affected
    # For this dataset, 0 is no data, 1 is cloud, 2 is normal water level
    # and 3 is overflow.
    threshold = 2.9

    # This is a scalar but will end up being a matrix
    I_sum = None
    projection = None
    geotransform = None
    total_days = len(hazard_files)
    ignored = 0

    print 'Accumulating layers'

    I_sum_shape = None
    for hazard_filename in hazard_files:
        if os.path.exists(hazard_filename):
            print " - Processing %s" % hazard_filename
            layer = read_layer(hazard_filename)

            # Extract data as numeric arrays
            D = layer.get_data(nan=0.0)  # Depth
            # Assign ones where it is affected
            I = numpy.where(D > threshold, 1, 0)

            # If this is the first file, use it to initialize the aggregated one and stop processing
            if I_sum is None:
                I_sum = I
                I_sum_shape = I_sum.shape
                projection = layer.get_projection()
                geotransform = layer.get_geotransform()
                continue

            # If it is not the first one, add it up if it has the right shape, otherwise, ignore it
            if I_sum_shape == I.shape:
                I_sum = I_sum + I
            else:
                # Add them to a list of ignored files
                ignored = ignored + 1
                print 'Ignoring file %s because it is incomplete' % hazard_filename

    # Create raster object and return
    R = Raster(I_sum,
               projection=projection,
               geotransform=geotransform,
               name='People affected',
               keywords={
                   'category': 'hazard',
                   'subcategory': 'flood',
                   'units': 'days',
                   'total_days': total_days,
                   'ignored': ignored,
               })
    return R
Exemplo n.º 10
0
def write_raster_data(data, projection, geotransform, filename, keywords=None):
    """Write array to raster file with specified metadata and one data layer

    Input:
        data: Numpy array containing grid data
        projection: WKT projection information
        geotransform: 6 digit vector
                      (top left x, w-e pixel resolution, rotation,
                       top left y, rotation, n-s pixel resolution).
                       See e.g. http://www.gdal.org/gdal_tutorial.html
        filename: Output filename
        keywords: Optional dictionary

    Note: The only format implemented is GTiff and the extension must be .tif
    """

    R = Raster(data, projection, geotransform, keywords=keywords)
    R.write_to_file(filename)
Exemplo n.º 11
0
def read_layer(filename):
    """Read spatial layer from file.
    This can be either raster or vector data.
    """
    _, ext = os.path.splitext(filename)
    if ext in ['.asc', '.tif', '.nc', '.adf']:
        return Raster(filename)
    else:
        msg = ('Could not read %s. '
               'Extension "%s" has not been implemented' % (filename, ext))
        raise ReadLayerError(msg)
    def run(self, layers):
        """Risk plugin for tsunami population
        """

        thresholds = [0.2, 0.3, 0.5, 0.8, 1.0]
        # threshold = 1  # Depth above which people are regarded affected [m]

        # Identify hazard and exposure layers
        inundation = get_hazard_layer(layers)  # Tsunami inundation [m]
        population = get_exposure_layer(layers)  # Population density

        # Extract data as numeric arrays
        D = inundation.get_data(nan=0.0)  # Depth
        P = population.get_data(nan=0.0, scaling=True)  # Population density

        # Calculate impact as population exposed to depths > 1m
        I_map = numpy.where(D > thresholds[-1], P, 0)

        # Generate text with result for this study
        number_of_people_affected = numpy.nansum(I_map.flat)

        # Do breakdown

        # Create report
        impact_summary = ('<table border="0" width="320px">'
                          '   <tr><th><b>%s</b></th><th><b>%s</b></th></th>'
                          '   <tr></tr>' %
                          ('Ambang batas', 'Jumlah orang terdampak'))

        counts = []
        for i, threshold in enumerate(thresholds):
            I = numpy.where(D > threshold, P, 0)
            counts.append(numpy.nansum(I.flat))

            impact_summary += '   <tr><td>%s m</td><td>%i</td></tr>' % (
                threshold, counts[i])

        impact_summary += '</table>'

        # Create raster object and return
        R = Raster(I_map,
                   projection=inundation.get_projection(),
                   geotransform=inundation.get_geotransform(),
                   name='People affected by more than 1m of inundation',
                   keywords={'impact_summary': impact_summary})
        return R
    def run(layers, teta=14.05, beta=0.17, zeta=2.15):
        """Risk plugin for earthquake fatalities

        Input
          H: Numerical array of hazard data
          E: Numerical array of exposure data
        """

        # Suppress warnings about invalid value in multiply and divide zero
        # http://comments.gmane.org/gmane.comp.python.numeric.general/43218
        # http://docs.scipy.org/doc/numpy/reference/generated/numpy.seterr.html
        old_numpy_setting = numpy.seterr(invalid='ignore')
        numpy.seterr(divide='ignore')

        # Identify input layers
        intensity = get_hazard_layer(layers)
        population = get_exposure_layer(layers)

        # Extract data
        H = intensity.get_data(nan=0)
        P = population.get_data(nan=0)

        # Calculate impact
        logHazard = 1 / beta * numpy.log(H / teta)

        # Convert array to be standard floats expected by cdf
        arrayout = numpy.array([[float(value) for value in row]
                                for row in logHazard])
        x = arrayout * P
        F = cdf(x)

        numpy.seterr(**old_numpy_setting)

        # Create new layer and return
        R = Raster(F,
                   projection=population.get_projection(),
                   geotransform=population.get_geotransform(),
                   name='Estimated fatalities')
        return R
Exemplo n.º 14
0
    def run(self):
        """Risk plugin for flood population evacuation.

        Counts number of people exposed to areas identified as flood prone

        :returns: Map of population exposed to flooding Table with number of
            people evacuated and supplies required.
        :rtype: tuple
        """
        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.hazard_class_mapping = self.hazard.keyword('value_map')

        # Get the IF parameters
        self._evacuation_percentage = (
            self.parameters['evacuation_percentage'].value)

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

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True

        # Check that affected field exists in hazard layer
        if (self.hazard_class_attribute in
                self.hazard.layer.get_attribute_names()):
            self.use_affected_field = True

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

        # Data for manipulating the covered_exposure layer
        new_covered_exposure_data = covered_exposure.get_data()
        covered_exposure_top_left = numpy.array([
            covered_exposure.get_geotransform()[0],
            covered_exposure.get_geotransform()[3]])
        covered_exposure_dimension = numpy.array([
            covered_exposure.get_geotransform()[1],
            covered_exposure.get_geotransform()[5]])

        # Count affected population per polygon, per category and total
        total_affected_population = 0
        for attr in interpolated_layer.get_data():
            affected = False
            if self.use_affected_field:
                row_affected_value = attr[self.hazard_class_attribute]
                if row_affected_value is not None:
                    affected = get_key_for_value(
                        row_affected_value, self.hazard_class_mapping)
            else:
                # assume that every polygon is affected (see #816)
                affected = self.wet

            if affected == self.wet:
                # Get population at this location
                population = attr[self.target_field]
                if not numpy.isnan(population):
                    population = float(population)
                    total_affected_population += population
            else:
                # If it's not affected, set the value of the impact layer to 0
                grid_point = attr['grid_point']
                index = numpy.floor(
                    (grid_point - covered_exposure_top_left) / (
                        covered_exposure_dimension)).astype(int)
                new_covered_exposure_data[index[1]][index[0]] = 0

        # Estimate number of people in need of evacuation
        if self.use_affected_field:
            affected_population = tr(
                'People within hazard field ("%s") of value "%s"') % (
                    self.hazard_class_attribute,
                    ','.join([
                        unicode(hazard_class) for
                        hazard_class in self.hazard_class_mapping[self.wet]
                    ]))
        else:
            affected_population = tr('People within any hazard polygon.')

        self.affected_population[affected_population] = (
            total_affected_population)

        self.total_population = int(
            numpy.nansum(self.exposure.layer.get_data(scaling=False)))
        self.unaffected_population = (
            self.total_population - self.total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]

        impact_table = impact_summary = self.html_report()

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(
            new_covered_exposure_data.flat[:], len(colours))

        # check for zero impact
        if total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        # For printing map purpose
        map_title = tr('People affected by flood prone areas')
        legend_title = tr('Population Count')
        legend_units = tr('(people per polygon)')
        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,
            'affected_population': total_affected_population,
            'total_population': self.total_population,
            'total_needs': self.total_needs
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Raster(
            data=new_covered_exposure_data,
            projection=covered_exposure.get_projection(),
            geotransform=covered_exposure.get_geotransform(),
            name=tr('People affected by flood prone areas'),
            keywords=impact_layer_keywords,
            style_info=style_info)
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 15
0
class ITBFatalityFunction(ImpactFunction):
    # noinspection PyUnresolvedReferences
    """Indonesian Earthquake Fatality Model.

    This model was developed by Institut Teknologi Bandung (ITB) and
    implemented by Dr. Hadi Ghasemi, Geoscience Australia.


    Reference:

    Indonesian Earthquake Building-Damage and Fatality Models and
    Post Disaster Survey Guidelines Development,
    Bali, 27-28 February 2012, 54pp.


    Algorithm:

    In this study, the same functional form as Allen (2009) is adopted
    to express fatality rate as a function of intensity (see Eq. 10 in the
    report). The Matlab built-in function (fminsearch) for  Nelder-Mead
    algorithm was used to estimate the model parameters. The objective
    function (L2G norm) that is minimised during the optimisation is the
    same as the one used by Jaiswal et al. (2010).

    The coefficients used in the indonesian model are
    x=0.62275231, y=8.03314466, zeta=2.15

    Allen, T. I., Wald, D. J., Earle, P. S., Marano, K. D., Hotovec, A. J.,
    Lin, K., and Hearne, M., 2009. An Atlas of ShakeMaps and population
    exposure catalog for earthquake loss modeling, Bull. Earthq. Eng. 7,
    701-718.

    Jaiswal, K., and Wald, D., 2010. An empirical model for global earthquake
    fatality estimation, Earthq. Spectra 26, 1017-1037.


    Caveats and limitations:

    The current model is the result of the above mentioned workshop and
    reflects the best available information. However, the current model
    has a number of issues listed below and is expected to evolve further
    over time.

    1 - The model is based on limited number of observed fatality
        rates during 4 past fatal events.
    2 - The model clearly over-predicts the fatality rates at
        intensities higher than VIII.
    3 - The model only estimates the expected fatality rate for a given
        intensity level; however the associated uncertainty for the proposed
        model is not addressed.
    4 - There are few known mistakes in developing the current model:
        - rounding MMI values to the nearest 0.5,
        - Implementing Finite-Fault models of candidate events, and
        - consistency between selected GMPEs with those in use by BMKG.
          These issues will be addressed by ITB team in the final report.

    Note: Because of these caveats, decisions should not be made solely on
    the information presented here and should always be verified by ground
    truthing and other reliable information sources.
    """

    _metadata = ITBFatalityMetadata()

    def __init__(self):
        super(ITBFatalityFunction, self).__init__()

        # AG: Use the proper minimum needs, update the parameters
        self.parameters = add_needs_parameters(self.parameters)
        self.hardcoded_parameters = OrderedDict([
            ('x', 0.62275231), ('y', 8.03314466),  # Model coefficients
            # Rates of people displaced for each MMI level
            ('displacement_rate', {
                1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 1.0,
                7: 1.0, 8: 1.0, 9: 1.0, 10: 1.0
            }),
            ('mmi_range', range(2, 10)),
            ('step', 0.5),
            # Threshold below which layer should be transparent
            ('tolerance', 0.01),
            ('calculate_displaced_people', True)
        ])

    def fatality_rate(self, mmi):
        """ITB method to compute fatality rate.

        :param mmi:
        """
        # As per email discussion with Ole, Trevor, Hadi, mmi < 4 will have
        # a fatality rate of 0 - Tim
        if mmi < 4:
            return 0

        x = self.hardcoded_parameters['x']
        y = self.hardcoded_parameters['y']
        # noinspection PyUnresolvedReferences
        return numpy.power(10.0, x * mmi - y)

    def run(self, layers=None):
        """Indonesian Earthquake Fatality Model.

        Input:

        :param layers: List of layers expected to contain,

                hazard: Raster layer of MMI ground shaking

                exposure: Raster layer of population count
        """
        self.validate()
        self.prepare(layers)

        displacement_rate = self.hardcoded_parameters['displacement_rate']

        # Tolerance for transparency
        tolerance = self.hardcoded_parameters['tolerance']

        # Extract input layers
        intensity = self.hazard
        population = self.exposure

        # Extract data grids
        hazard = intensity.get_data()   # Ground Shaking
        exposure = population.get_data(scaling=True)  # Population Density

        # Calculate people affected by each MMI level
        # FIXME (Ole): this range is 2-9. Should 10 be included?
        mmi_range = self.hardcoded_parameters['mmi_range']
        number_of_exposed = {}
        number_of_displaced = {}
        number_of_fatalities = {}

        # Calculate fatality rates for observed Intensity values (hazard
        # based on ITB power model
        mask = numpy.zeros(hazard.shape)
        for mmi in mmi_range:
            # Identify cells where MMI is in class i and
            # count people affected by this shake level
            mmi_matches = numpy.where(
                (hazard > mmi - self.hardcoded_parameters['step']) * (
                    hazard <= mmi + self.hardcoded_parameters['step']),
                exposure, 0)

            # Calculate expected number of fatalities per level
            fatality_rate = self.fatality_rate(mmi)

            fatalities = fatality_rate * mmi_matches

            # Calculate expected number of displaced people per level
            try:
                displacements = displacement_rate[mmi] * mmi_matches
            except KeyError, e:
                msg = 'mmi = %i, mmi_matches = %s, Error msg: %s' % (
                    mmi, str(mmi_matches), str(e))
                # noinspection PyExceptionInherit
                raise InaSAFEError(msg)

            # Adjust displaced people to disregard fatalities.
            # Set to zero if there are more fatalities than displaced.
            displacements = numpy.where(
                displacements > fatalities, displacements - fatalities, 0)

            # Sum up numbers for map
            mask += displacements   # Displaced

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = numpy.nansum(mmi_matches.flat)
            number_of_displaced[mmi] = numpy.nansum(displacements.flat)
            # noinspection PyUnresolvedReferences
            number_of_fatalities[mmi] = numpy.nansum(fatalities.flat)

        # Set resulting layer to NaN when less than a threshold. This is to
        # achieve transparency (see issue #126).
        mask[mask < tolerance] = numpy.nan

        # Total statistics
        total, rounding = population_rounding_full(numpy.nansum(exposure.flat))

        # Compute number of fatalities
        fatalities = population_rounding(numpy.nansum(
            number_of_fatalities.values()))
        # As per email discussion with Ole, Trevor, Hadi, total fatalities < 50
        # will be rounded down to 0 - Tim
        if fatalities < 50:
            fatalities = 0

        # Compute number of people displaced due to building collapse
        displaced = population_rounding(numpy.nansum(
            number_of_displaced.values()))

        # Generate impact report
        table_body = [self.question]

        # Add total fatality estimate
        s = format_int(fatalities)
        table_body.append(TableRow([tr('Number of fatalities'), s],
                                   header=True))

        if self.hardcoded_parameters['calculate_displaced_people']:
            # Add total estimate of people displaced
            s = format_int(displaced)
            table_body.append(TableRow([tr('Number of people displaced'), s],
                                       header=True))
        else:
            displaced = 0

        # Add estimate of total population in area
        s = format_int(int(total))
        table_body.append(TableRow([tr('Total number of people'), s],
                                   header=True))

        minimum_needs = [
            parameter.serialize() for parameter in
            self.parameters['minimum needs']
        ]

        # Generate impact report for the pdf map
        table_body = [
            self.question, TableRow(
                [tr('Fatalities'), '%s' % format_int(fatalities)],
                header=True),
            TableRow(
                [tr('People displaced'), '%s' % format_int(displaced)],
                header=True),
            TableRow(tr('Map shows the estimation of displaced population'))]

        total_needs = evacuated_population_needs(
            displaced, minimum_needs)
        for frequency, needs in total_needs.items():
            table_body.append(TableRow(
                [
                    tr('Needs should be provided %s' % frequency),
                    tr('Total')
                ],
                header=True))
            for resource in needs:
                table_body.append(TableRow([
                    tr(resource['table name']),
                    format_int(resource['amount'])]))
        table_body.append(TableRow(tr('Provenance'), header=True))
        table_body.append(TableRow(self.parameters['provenance']))

        table_body.append(TableRow(tr('Action Checklist:'), header=True))

        if fatalities > 0:
            table_body.append(tr('Are there enough victim identification '
                                 'units available for %s people?') %
                              format_int(fatalities))
        if displaced > 0:
            table_body.append(tr('Are there enough shelters and relief items '
                                 'available for %s people?')
                              % format_int(displaced))
            table_body.append(TableRow(tr('If yes, where are they located and '
                                          'how will we distribute them?')))
            table_body.append(TableRow(tr('If no, where can we obtain '
                                          'additional relief items from and '
                                          'how will we transport them?')))

        # Extend impact report for on-screen display
        table_body.extend([TableRow(tr('Notes'), header=True),
                           tr('Total population: %s') % format_int(total),
                           tr('People are considered to be displaced if '
                              'they experience and survive a shake level'
                              'of more than 5 on the MMI scale '),
                           tr('Minimum needs are defined in BNPB '
                              'regulation 7/2008'),
                           tr('The fatality calculation assumes that '
                              'no fatalities occur for shake levels below 4 '
                              'and fatality counts of less than 50 are '
                              'disregarded.'),
                           tr('All values are rounded up to the nearest '
                              'integer in order to avoid representing human '
                              'lives as fractions.')])

        table_body.append(TableRow(tr('Notes'), header=True))
        table_body.append(tr('Fatality model is from '
                             'Institute of Teknologi Bandung 2012.'))
        table_body.append(
            tr('Population numbers rounded up to the nearest %s.') % rounding)

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

        # check for zero impact
        if numpy.nanmax(mask) == 0 == numpy.nanmin(mask):
            table_body = [
                self.question,
                TableRow([tr('Fatalities'), '%s' % format_int(fatalities)],
                         header=True)]
            my_message = Table(table_body).toNewlineFreeString()
            raise ZeroImpactException(my_message)

        # Create style
        colours = ['#EEFFEE', '#FFFF7F', '#E15500', '#E4001B', '#730000']
        classes = create_classes(mask.flat[:], len(colours))
        interval_classes = humanize_class(classes)
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            style_class['quantity'] = classes[i]
            if i == 0:
                transparency = 100
            else:
                transparency = 30
            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('Earthquake impact to population')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(people per cell)')
        legend_title = tr('Population Count')

        # Create raster object and return
        raster = Raster(
            mask,
            projection=population.get_projection(),
            geotransform=population.get_geotransform(),
            keywords={
                'impact_summary': impact_summary,
                'total_population': total,
                'total_fatalities': fatalities,
                'fatalities_per_mmi': number_of_fatalities,
                'exposed_per_mmi': number_of_exposed,
                'displaced_per_mmi': number_of_displaced,
                'impact_table': impact_table,
                'map_title': map_title,
                'legend_notes': legend_notes,
                'legend_units': legend_units,
                'legend_title': legend_title,
                'total_needs': total_needs},
            name=tr('Estimated displaced population per cell'),
            style_info=style_info)
        self._impact = raster
        return raster
Exemplo n.º 16
0
def interpolate_polygon_raster(source,
                               target,
                               layer_name=None,
                               attribute_name=None):
    """Interpolate from polygon layer to raster data.

    .. note:
        Each point in the resulting dataset will have an attribute
        'polygon_id' which refers to the polygon it belongs to and
        'grid_point' which refers to the grid point of the target.

    :param source: Polygon data set.
    :type source: Vector

    :param target: Raster data set.
    :type target: Raster

    :param layer_name: Optional name of returned interpolated layer. If None
        the name of source is used for the returned layer.
    :type layer_name: basestring

    :param attribute_name: Name for new attribute. If None (default) the name
        of layer target is used
    :type attribute_name: basestring

    :returns: Tuple of Vector (points located as target with values
        interpolated from source) and Raster  (raster data that are coincide
        with the source)
    :rtype: Vector
    """
    # Input checks
    verify(source.is_polygon_data)
    verify(target.is_raster)

    # Run underlying clipping algorithm
    polygon_geometry = source.get_geometry(as_geometry_objects=True)

    polygon_attributes = source.get_data()
    covered_source, covered_target = clip_grid_by_polygons(
        target.get_data(scaling=False), target.get_geotransform(),
        polygon_geometry)

    # Create one new point layer with interpolated attributes
    new_geometry = []
    new_attributes = []
    for i, (geometry, values) in enumerate(covered_source):
        # For each polygon assign attributes to points that fall inside it
        for j, geom in enumerate(geometry):
            attr = polygon_attributes[i].copy()  # Attributes for this polygon
            attr[attribute_name] = values[j]  # Attribute value from grid cell
            attr['polygon_id'] = i  # Store id for associated polygon
            attr['grid_point'] = geom  # Store grid point for associated grid
            new_attributes.append(attr)
            new_geometry.append(geom)

    interpolated_layer = Vector(data=new_attributes,
                                projection=source.get_projection(),
                                geometry=new_geometry,
                                name=layer_name)

    covered_target = Raster(data=covered_target,
                            projection=target.get_projection(),
                            geotransform=target.get_geotransform(),
                            name=layer_name)

    return interpolated_layer, covered_target
Exemplo n.º 17
0
    def run(self):
        """Risk plugin for flood population evacuation.

        Counts number of people exposed to areas identified as flood prone

        :returns: Map of population exposed to flooding Table with number of
            people evacuated and supplies required.
        :rtype: tuple
        """

        # Get parameters from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')

        # Get the IF parameters
        self._evacuation_percentage = (
            self.parameters['evacuation_percentage'].value)

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

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True

        # Check that affected field exists in hazard layer
        if (self.hazard_class_attribute in
                self.hazard.layer.get_attribute_names()):
            self.use_affected_field = True

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

        # Data for manipulating the covered_exposure layer
        new_covered_exposure_data = covered_exposure.get_data()
        covered_exposure_top_left = numpy.array([
            covered_exposure.get_geotransform()[0],
            covered_exposure.get_geotransform()[3]])
        covered_exposure_dimension = numpy.array([
            covered_exposure.get_geotransform()[1],
            covered_exposure.get_geotransform()[5]])

        # Count affected population per polygon, per category and total
        total_affected_population = 0
        for attr in interpolated_layer.get_data():
            affected = False
            if self.use_affected_field:
                row_affected_value = attr[self.hazard_class_attribute]
                if row_affected_value is not None:
                    affected = get_key_for_value(
                        row_affected_value, self.hazard_class_mapping)
            else:
                # assume that every polygon is affected (see #816)
                affected = self.wet

            if affected == self.wet:
                # Get population at this location
                population = attr[self.target_field]
                if not numpy.isnan(population):
                    population = float(population)
                    total_affected_population += population
            else:
                # If it's not affected, set the value of the impact layer to 0
                grid_point = attr['grid_point']
                index = numpy.floor(
                    (grid_point - covered_exposure_top_left) / (
                        covered_exposure_dimension)).astype(int)
                new_covered_exposure_data[index[1]][index[0]] = 0

        # Estimate number of people in need of evacuation
        if self.use_affected_field:
            affected_population = tr(
                'People within hazard field ("%s") of value "%s"') % (
                    self.hazard_class_attribute,
                    ','.join([
                        unicode(hazard_class) for
                        hazard_class in self.hazard_class_mapping[self.wet]
                    ]))
        else:
            affected_population = tr('People within any hazard polygon.')

        self.affected_population[affected_population] = (
            total_affected_population)

        self.total_population = int(
            numpy.nansum(self.exposure.layer.get_data(scaling=False)))
        self.unaffected_population = (
            self.total_population - self.total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(
            new_covered_exposure_data.flat[:], len(colours))

        # check for zero impact
        if total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        impact_data = self.generate_data()

        extra_keywords = {
            'target_field': self.target_field,
            'map_title': self.metadata().key('map_title'),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'affected_population': total_affected_population,
            'total_population': self.total_population,
            'total_needs': self.total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster layer and return
        impact_layer = Raster(
            data=new_covered_exposure_data,
            projection=covered_exposure.get_projection(),
            geotransform=covered_exposure.get_geotransform(),
            name=self.metadata().key('layer_name'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 18
0
    def run(self):
        """Plugin for impact of population as derived by continuous hazard.

        Hazard is reclassified into 3 classes based on the extrema provided
        as impact function parameters.

        Counts number of people exposed to each category of the hazard

        :returns:
          Map of population exposed to high category
          Table with number of people in each category
        """

        thresholds = [
            p.value for p in self.parameters['Categorical thresholds'].value]

        # Thresholds must contain 3 thresholds
        if len(thresholds) != 3:
            raise FunctionParametersError(
                'The thresholds must consist of 3 values.')

        # Thresholds must monotonically increasing
        monotonically_increasing_flag = all(
            x < y for x, y in zip(thresholds, thresholds[1:]))
        if not monotonically_increasing_flag:
            raise FunctionParametersError(
                'Each threshold should be larger than the previous.')

        # The 3 categories
        low_t = thresholds[0]
        medium_t = thresholds[1]
        high_t = thresholds[2]

        # Extract data as numeric arrays
        hazard_data = self.hazard.layer.get_data(nan=True)  # Category
        if has_no_data(hazard_data):
            self.no_data_warning = True

        # Calculate impact as population exposed to each category
        exposure_data = self.exposure.layer.get_data(nan=True, scaling=True)
        if has_no_data(exposure_data):
            self.no_data_warning = True

        # Make 3 data for each zone. Get the value of the exposure if the
        # exposure is in the hazard zone, else just assign 0
        low_exposure = numpy.where(hazard_data < low_t, exposure_data, 0)
        medium_exposure = numpy.where(
            (hazard_data >= low_t) & (hazard_data < medium_t),
            exposure_data, 0)
        high_exposure = numpy.where(
            (hazard_data >= medium_t) & (hazard_data <= high_t),
            exposure_data, 0)
        impacted_exposure = low_exposure + medium_exposure + high_exposure

        # Count totals
        self.total_population = int(numpy.nansum(exposure_data))
        self.affected_population[
            tr('Population in high hazard areas')] = int(
                numpy.nansum(high_exposure))
        self.affected_population[
            tr('Population in medium hazard areas')] = int(
                numpy.nansum(medium_exposure))
        self.affected_population[
            tr('Population in low hazard areas')] = int(
                numpy.nansum(low_exposure))
        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)

        # Don't show digits less than a 1000
        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]
        total_needs = self.total_needs

        # Style for impact layer
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00',
            '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(impacted_exposure.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]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(
            target_field=None,
            style_classes=style_classes,
            style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.metadata().key('map_title'),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            data=impacted_exposure,
            projection=self.hazard.layer.get_projection(),
            geotransform=self.hazard.layer.get_geotransform(),
            name=self.metadata().key('layer_name'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 19
0
    def run(self):
        """Risk plugin for tsunami population evacuation.

        Counts number of people exposed to tsunami levels exceeding
        specified threshold.

        :returns: Map of population exposed to tsunami levels exceeding the
            threshold. Table with number of people evacuated and supplies
            required.
        :rtype: tuple
        """

        # Determine depths above which people are regarded affected [m]
        # Use thresholds from inundation layer if specified
        thresholds = self.parameters['thresholds'].value

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

        # Extract data as numeric arrays
        data = self.hazard.layer.get_data(nan=True)  # Depth
        if has_no_data(data):
            self.no_data_warning = True

        # Calculate impact as population exposed to depths > max threshold
        population = self.exposure.layer.get_data(nan=True, scaling=True)
        if has_no_data(population):
            self.no_data_warning = True

        # merely initialize
        impact = None
        for i, lo in enumerate(thresholds):
            if i == len(thresholds) - 1:
                # The last threshold
                thresholds_name = tr(
                    'People in >= %.1f m of water') % lo
                impact = medium = numpy.where(data >= lo, population, 0)
                self.impact_category_ordering.append(thresholds_name)
                self._evacuation_category = thresholds_name
            else:
                # Intermediate thresholds
                hi = thresholds[i + 1]
                thresholds_name = tr(
                    'People in %.1f m to %.1f m of water' % (lo, hi))
                medium = numpy.where((data >= lo) * (data < hi), population, 0)

            # Count
            val = int(numpy.nansum(medium))
            self.affected_population[thresholds_name] = val

        # Put the deepest area in top #2385
        self.impact_category_ordering.reverse()

        # Carry the no data values forward to the impact layer.
        impact = numpy.where(numpy.isnan(population), numpy.nan, impact)
        impact = numpy.where(numpy.isnan(data), numpy.nan, impact)

        # Count totals
        self.total_population = int(numpy.nansum(population))
        self.unaffected_population = (
            self.total_population - self.total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]

        # check for zero impact
        if numpy.nanmax(impact) == 0 == numpy.nanmin(impact):
            message = m.Message()
            message.add(self.question)
            message.add(tr('No people in %.1f m of water') % thresholds[-1])
            message = message.to_html(suppress_newlines=True)
            raise ZeroImpactException(message)

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00',
            '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(impact.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], 'Low')
            elif i == 4:
                label = create_label(interval_classes[i], 'Medium')
            elif i == 7:
                label = create_label(interval_classes[i], 'High')
            else:
                label = create_label(interval_classes[i])
            style_class['label'] = label
            style_class['quantity'] = classes[i]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(
            target_field=None,
            style_classes=style_classes,
            style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.metadata().key('map_title'),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'evacuated': self.total_evacuated,
            'total_needs': self.total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            impact,
            projection=self.hazard.layer.get_projection(),
            geotransform=self.hazard.layer.get_geotransform(),
            name=self.metadata().key('layer_name'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 20
0
def convert_netcdf2tif(filename, n, verbose=False, output_dir=None):

    """Convert netcdf to tif aggregating first n bands

    Args
        * filename: NetCDF multiband raster with extension .nc
        * n: Positive integer determining how many bands to use
        * verbose: Boolean flag controlling whether diagnostics
          will be printed to screen. This is useful when run from
          a command line script.

    Returns
        * Raster file in tif format. Each pixel will be the maximum
          of that pixel in the first n bands in the input file.

    """

    if not isinstance(filename, basestring):
        msg = 'Argument filename should be a string. I got %s' % filename
        raise RuntimeError(msg)

    basename, ext = os.path.splitext(filename)
    msg = ('Expected NetCDF file with extension .nc - '
           'Instead I got %s' % filename)
    if ext != '.nc':
        raise RuntimeError(msg)

    try:
        n = int(n)
    except:
        msg = 'Argument N should be an integer. I got %s' % n
        raise RuntimeError(msg)

    if verbose:
        print filename, n, 'hours'

    # Read NetCDF file
    fid = NetCDFFile(filename)
    dimensions = fid.dimensions.keys()
    variables = fid.variables.keys()

    title = getattr(fid, 'title')
    institution = getattr(fid, 'institution')
    source = getattr(fid, 'source')
    history = getattr(fid, 'history')
    references = getattr(fid, 'references')
    conventions = getattr(fid, 'Conventions')
    coordinate_system = getattr(fid, 'coordinate_system')

    if verbose:
        print 'Read from %s' % filename
        print 'Title: %s' % title
        print 'Institution: %s' % institution
        print 'Source: %s' % source
        print 'History: %s' % history
        print 'References: %s' % references
        print 'Conventions: %s' % conventions
        print 'Coordinate system: %s' % coordinate_system

        print 'Dimensions: %s' % dimensions
        print 'Variables:  %s' % variables

    # Get data
    x = fid.variables['x'][:]
    y = fid.variables['y'][:]
    # t = fid.variables['time'][:]
    inundation_depth = fid.variables['Inundation_Depth'][:]

    T = inundation_depth.shape[0]  # Number of time steps
    M = inundation_depth.shape[1]  # Steps in the y direction
    N = inundation_depth.shape[2]  # Steps in the x direction

    if n > T:
        msg = ('You requested %i hours prediction, but the '
               'forecast only contains %i hours' % (n, T))
        raise RuntimeError(msg)

    # Compute the max of the first n timesteps
    A = numpy.zeros((M, N), dtype='float')
    for i in range(n):
        B = inundation_depth[i, :, :]
        A = numpy.maximum(A, B)

        # Calculate overall maximal value
        total_max = numpy.max(A[:])
        #print i, numpy.max(B[:]), total_max

    geotransform = raster_geometry_to_geotransform(x, y)

    # Write result to tif file
    # NOTE: This assumes a default projection (WGS 84, geographic)
    date = os.path.split(basename)[-1].split('_')[0]

    if verbose:
        print 'Overall max depth over %i hours: %.2f m' % (n, total_max)
        print 'Geotransform', geotransform
        print 'date', date

    # Flip array upside down as it comes with rows ordered from south to north
    A = numpy.flipud(A)

    R = Raster(data=A,
               geotransform=geotransform,
               keywords={'category': 'hazard',
                         'subcategory': 'flood',
                         'unit': 'm',
                         'title': ('%d hour flood forecast grid '
                                   'in Jakarta at %s' % (n, date))})

    tif_filename = '%s_%d_hours_max_%.2f.tif' % (basename, n, total_max)
    if output_dir is not None:
        subdir_name = os.path.splitext(os.path.basename(tif_filename))[0]
        shapefile_dir = os.path.join(output_dir, subdir_name)
        if not os.path.isdir(shapefile_dir):
            os.mkdir(shapefile_dir)
        tif_filename = os.path.join(shapefile_dir, subdir_name + '.tif')

    R.write_to_file(tif_filename)

    if verbose:
        print 'Success: %d hour forecast written to %s' % (n, R.filename)

    return tif_filename
Exemplo n.º 21
0
def start(west,north,east,south, since, until=None, data_dir=None, population=None):
    
    bbox = (west, north, east, south)

    year, month, day = [int(x) for x in since.split('-')]
    since = datetime.date(year, month, day)

    if not isinstance(until, datetime.date):
        year, month, day = [int(x) for x in until.split('-')]
        until = datetime.date(year, month, day)
    else:
        until = until

    # Make sure the inputs are divisible by 10.
    for item in bbox:
        msg = "%d is not divisible by 10." % item
        assert int(item) % 10 == 0, msg

    the_viewports = viewports(bbox)
    the_timespan = timespan(since, until)

    data_dir = os.path.abspath(data_dir)

    if not os.path.exists(data_dir):
        os.mkdir(data_dir)

    print 'Downloading layers per day'
    # Download the layers for the given viewport and timespan.
    download(the_viewports, the_timespan, data_dir)

    print 'Merging layers per day'
    merged_files = merge(the_timespan, data_dir)

    flood_filename = os.path.join(data_dir, 'flood_severity.tif')

    if not os.path.exists(flood_filename):
        if len(merged_files) > 0:
            # Add all the pixels with a value higher than 3.
            #accumulate(merged_files, flood_filename, threshold=3)
            flooded = _flood_severity(merged_files)
            flooded.write_to_file(flood_filename)

            subprocess.call(['gdal_merge.py',
                     '-co', 'compress=packbits',
                     '-o', 'flood_severity_compressed.tif',
                     '-ot', 'Byte',
                     flood_filename], stdout=open(os.devnull, 'w'))
            os.remove(flood_filename)
            os.rename('flood_severity_compressed.tif', flood_filename)
        else:
            raise Exception('No merged files found for %s' % the_timespan)
    
    population_file = os.path.join(data_dir, population)
    population_object = Raster(population_file)
    # get population bbox
    pop_bbox = population_object.get_bounding_box()

    # get resolutions and pick the best
    pop_resolution = population_object.get_resolution()[0]

    hazard_object = Raster(flood_filename)
    hazard_resolution = hazard_object.get_resolution()[0]
    hazard_bbox = hazard_object.get_bounding_box()

    if pop_bbox[0] > bbox[0] and pop_bbox[1] > bbox[1] and pop_bbox[2] < bbox[2] and pop_bbox[3] < bbox[3]:
        hazard_file = clip(flood_filename, pop_bbox, cellSize=pop_resolution)
        exposure_layer = population_file
    else:
        hazard_file = clip(flood_filename, hazard_bbox, cellSize=pop_resolution)
        exposure_layer = clip(population_file, hazard_bbox, cellSize=None)    

    basename, ext = os.path.splitext(hazard_file)
    keywords_file = basename + '.keywords'

    if not os.path.exists(keywords_file):
        with open(keywords_file, 'w') as f:
            f.write(FLOOD_KEYWORDS)

    impact = calculate(hazard_file, exposure_layer)

    impact.write_to_file('impact.tif')

    count = impact.keywords['count']
    pretty_date = until.strftime('%a %d, %b %Y')
    print pretty_date, "|", "People affected: %s / %s" % (count, impact.keywords['total'])
class ITBFatalityFunctionConfigurable(FunctionProvider):
    """Indonesian Earthquake Fatality Model

    This model was developed by Institut Tecknologi Bandung (ITB) and
    implemented by Dr Hadi Ghasemi, Geoscience Australia


    Reference:

    Indonesian Earthquake Building-Damage and Fatality Models and
    Post Disaster Survey Guidelines Development,
    Bali, 27-28 February 2012, 54pp.


    Algorithm:

    In this study, the same functional form as Allen (2009) is adopted
    to express fatality rate as a function of intensity (see Eq. 10 in the
    report). The Matlab built-in function (fminsearch) for  Nelder-Mead
    algorithm is used to estimate the model parameters. The objective
    function (L2G norm) that is minimised during the optimisation is the
    same as the one used by Jaiswal et al. (2010).

    The coefficients used in the indonesian model are
    x=0.62275231, y=8.03314466, zeta=2.15

    Allen, T. I., Wald, D. J., Earle, P. S., Marano, K. D., Hotovec, A. J.,
    Lin, K., and Hearne, M., 2009. An Atlas of ShakeMaps and population
    exposure catalog for earthquake loss modeling, Bull. Earthq. Eng. 7,
    701-718.

    Jaiswal, K., and Wald, D., 2010. An empirical model for global earthquake
    fatality estimation, Earthq. Spectra 26, 1017-1037.


    Caveats and limitations:

    The current model is the result of the above mentioned workshop and
    reflects the best available information. However, the current model
    has a number of issues listed below and is expected to evolve further
    over time.

    1 - The model is based on limited number of observed fatality
        rates during 4 past fatal events.
    2 - The model clearly over-predicts the fatality rates at
        intensities higher than VIII.
    3 - The model only estimates the expected fatality rate for a given
        intensity level; however the associated uncertainty for the proposed
        model is not addressed.
    4 - There are few known mistakes in developing the current model:
        - rounding MMI values to the nearest 0.5,
        - Implementing Finite-Fault models of candidate events, and
        - consistency between selected GMPEs with those in use by BMKG.
          These issues will be addressed by ITB team in the final report.


    :author Hadi Ghasemi
    :rating 3

    :param requires category=='hazard' and \
                    subcategory=='earthquake' and \
                    layertype=='raster' and \
                    unit=='MMI'

    :param requires category=='exposure' and \
                    subcategory=='population' and \
                    layertype=='raster'

    """

    title = tr('Die or be displaced')
    defaults = get_defaults()
    parameters = OrderedDict([
        ('x', 0.62275231),
        ('y', 8.03314466),  # Model coefficients
        # Rates of people displaced for each MMI level
        ('displacement_rate', {
            1: 0,
            2: 0,
            3: 0,
            4: 0,
            5: 0,
            6: 1.0,
            7: 1.0,
            8: 1.0,
            9: 1.0,
            10: 1.0
        }),
        # Threshold below which layer should be transparent
        ('tolerance', 0.01),
        ('calculate_displaced_people', True),
        ('postprocessors',
         OrderedDict([
             ('Gender', {
                 'on': True
             }),
             ('Age', {
                 'on':
                 True,
                 'params':
                 OrderedDict([('youth_ratio', defaults['YOUTH_RATIO']),
                              ('adult_ratio', defaults['ADULT_RATIO']),
                              ('elder_ratio', defaults['ELDER_RATIO'])])
             })
         ]))
    ])

    def run(self, layers):
        """Indonesian Earthquake Fatality Model

        Input
          layers: List of layers expected to contain
              H: Raster layer of MMI ground shaking
              P: Raster layer of population density

        """

        # Establish model coefficients
        x = self.parameters['x']
        y = self.parameters['y']

        # Define percentages of people being displaced at each mmi level
        displacement_rate = self.parameters['displacement_rate']

        # Tolerance for transparency
        tolerance = self.parameters['tolerance']

        # Extract input layers
        intensity = get_hazard_layer(layers)
        population = get_exposure_layer(layers)

        question = get_question(intensity.get_name(), population.get_name(),
                                self)

        # Extract data grids
        H = intensity.get_data()  # Ground Shaking
        P = population.get_data(scaling=True)  # Population Density

        # Calculate population affected by each MMI level
        # FIXME (Ole): this range is 2-9. Should 10 be included?
        mmi_range = range(2, 10)
        number_of_exposed = {}
        number_of_displaced = {}
        number_of_fatalities = {}

        # Calculate fatality rates for observed Intensity values (H
        # based on ITB power model
        R = numpy.zeros(H.shape)
        for mmi in mmi_range:

            # Identify cells where MMI is in class i
            mask = (H > mmi - 0.5) * (H <= mmi + 0.5)

            # Count population affected by this shake level
            I = numpy.where(mask, P, 0)

            # Calculate expected number of fatalities per level
            fatality_rate = numpy.power(10.0, x * mmi - y)
            F = fatality_rate * I

            # Calculate expected number of displaced people per level
            try:
                D = displacement_rate[mmi] * I
            except KeyError, e:
                msg = 'mmi = %i, I = %s, Error msg: %s' % (mmi, str(I), str(e))
                raise InaSAFEError(msg)

            # Adjust displaced people to disregard fatalities.
            # Set to zero if there are more fatalities than displaced.
            D = numpy.where(D > F, D - F, 0)

            # Sum up numbers for map
            R += D  # Displaced

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = numpy.nansum(I.flat)
            number_of_displaced[mmi] = numpy.nansum(D.flat)
            number_of_fatalities[mmi] = numpy.nansum(F.flat)

        # Set resulting layer to NaN when less than a threshold. This is to
        # achieve transparency (see issue #126).
        R[R < tolerance] = numpy.nan

        # Total statistics
        total = int(round(numpy.nansum(P.flat) / 1000) * 1000)

        # Compute number of fatalities
        fatalities = int(
            round(numpy.nansum(number_of_fatalities.values()) / 1000)) * 1000

        # Compute number of people displaced due to building collapse
        displaced = int(
            round(numpy.nansum(number_of_displaced.values()) / 1000)) * 1000

        # Generate impact report
        table_body = [question]

        # Add total fatality estimate
        s = str(int(fatalities)).rjust(10)
        table_body.append(
            TableRow([tr('Number of fatalities'), s], header=True))

        if self.parameters['calculate_displaced_people']:
            # Add total estimate of people displaced
            s = str(int(displaced)).rjust(10)
            table_body.append(
                TableRow([tr('Number of people displaced'), s], header=True))
        else:
            displaced = 0

        # Add estimate of total population in area
        s = str(int(total)).rjust(10)
        table_body.append(
            TableRow([tr('Total number of people'), s], header=True))

        # Calculate estimated needs based on BNPB Perka 7/2008 minimum bantuan
        rice = displaced * 2.8
        drinking_water = displaced * 17.5
        water = displaced * 67
        family_kits = displaced / 5
        toilets = displaced / 20

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([tr('Fatalities'), '%i' % fatalities], header=True),
            TableRow([tr('People displaced'),
                      '%i' % displaced], header=True),
            TableRow(
                tr('Map shows density estimate of '
                   'displaced population')),
            TableRow([tr('Needs per week'), tr('Total')], header=True),
            [tr('Rice [kg]'), int(rice)],
            [tr('Drinking Water [l]'),
             int(drinking_water)], [tr('Clean Water [l]'),
                                    int(water)],
            [tr('Family Kits'), int(family_kits)],
            [tr('Toilets'), int(toilets)]
        ]
        impact_table = Table(table_body).toNewlineFreeString()

        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        if fatalities > 0:
            table_body.append(
                tr('Are there enough victim identification '
                   'units available for %i people?') % fatalities)
        if displaced > 0:
            table_body.append(
                tr('Are there enough shelters and relief items '
                   'available for %i people?') % displaced)
            table_body.append(
                TableRow(
                    tr('If yes, where are they located and '
                       'how will we distribute them?')))
            table_body.append(
                TableRow(
                    tr('If no, where can we obtain '
                       'additional relief items from and '
                       'how will we transport them?')))

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Total population: %i') % total,
            tr('People are considered to be displaced if '
               'they experience and survive a shake level'
               'of more than 5 on the MMI scale '),
            tr('Minimum needs are defined in BNPB '
               'regulation 7/2008')
        ])

        impact_summary = Table(table_body).toNewlineFreeString()
        map_title = tr('People in need of evacuation')

        table_body.append(TableRow(tr('Notes'), header=True))
        table_body.append(
            tr('Fatality model is from '
               'Institute of Teknologi Bandung 2012.'))
        table_body.append(tr('Population numbers rounded to nearest 1000.'))

        impact_summary = Table(table_body).toNewlineFreeString()
        impact_table = impact_summary
        map_title = tr('Earthquake impact to population')

        # Create style info dynamically
        classes = numpy.linspace(numpy.nanmin(R.flat[:]),
                                 numpy.nanmax(R.flat[:]), 5)

        style_classes = [
            dict(colour='#EEFFEE',
                 quantity=classes[0],
                 transparency=100,
                 label=tr('%.2f people/cell') % classes[0]),
            dict(colour='#FFFF7F', quantity=classes[1], transparency=30),
            dict(colour='#E15500',
                 quantity=classes[2],
                 transparency=30,
                 label=tr('%.2f people/cell') % classes[2]),
            dict(colour='#E4001B', quantity=classes[3], transparency=30),
            dict(colour='#730000',
                 quantity=classes[4],
                 transparency=30,
                 label=tr('%.2f people/cell') % classes[4])
        ]
        style_info = dict(target_field=None, style_classes=style_classes)

        # Create new layer and return
        L = Raster(R,
                   projection=population.get_projection(),
                   geotransform=population.get_geotransform(),
                   keywords={
                       'impact_summary': impact_summary,
                       'total_population': total,
                       'total_fatalities': fatalities,
                       'impact_table': impact_table,
                       'map_title': map_title
                   },
                   name=tr('Estimated displaced population'),
                   style_info=style_info)

        # Maybe return a shape file with contours instead
        return L
Exemplo n.º 23
0
    def run(self):
        """Run classified population evacuation Impact Function.

        Counts number of people exposed to each hazard zones.

        :returns: Map of population exposed to each hazard zone.
            The returned dict will include a table with number of people
            evacuated and supplies required.
        :rtype: dict

        :raises:
            * Exception - When hazard layer is not vector layer
        """

        # Value from layer's keywords
        self.hazard_class_attribute = self.hazard.keyword('field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')
        # TODO: Remove check to self.validate (Ismail)
        # Input checks
        message = tr(
            'Input hazard must be a polygon layer. I got %s with layer type '
            '%s' % (self.hazard.name, self.hazard.layer.get_geometry_name()))
        if not self.hazard.layer.is_polygon_data:
            raise Exception(message)

        # Check if hazard_class_attribute exists in hazard_layer
        if (self.hazard_class_attribute
                not in self.hazard.layer.get_attribute_names()):
            message = tr(
                'Hazard data %s does not contain expected hazard '
                'zone attribute "%s". Please change it in the option. ' %
                (self.hazard.name, self.hazard_class_attribute))
            # noinspection PyExceptionInherit
            raise InaSAFEError(message)

        # 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_population = 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_population[vector_hazard_class['name']] = 0

        # Interpolated layer represents grid cell that lies in the polygon
        interpolated_layer, covered_exposure_layer = \
            assign_hazard_values_to_exposure_data(
                self.hazard.layer,
                self.exposure.layer,
                attribute_name=self.target_field
            )

        # Count total affected population per hazard zone
        for row in interpolated_layer.get_data():
            # Get population at this location
            population = row[self.target_field]
            if not numpy.isnan(population):
                population = float(population)
                # Update population count for this hazard zone
                hazard_value = get_key_for_value(
                    row[self.hazard_class_attribute],
                    self.hazard_class_mapping)
                if not hazard_value:
                    hazard_value = self._not_affected_value
                else:
                    self.affected_population[hazard_value] += population

        # Count total population from exposure layer
        self.total_population = int(
            numpy.nansum(self.exposure.layer.get_data()))

        # Count total affected population
        total_affected_population = self.total_affected_population
        self.unaffected_population = (self.total_population -
                                      total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in filter_needs_parameters(
                self.parameters['minimum needs'])
        ]

        # check for zero impact
        if total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(covered_exposure_layer.get_data().flat[:],
                                 len(colours))
        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        impact_data = self.generate_data()

        extra_keywords = {
            'target_field': self.target_field,
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title')
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Raster(
            data=covered_exposure_layer.get_data(),
            projection=covered_exposure_layer.get_projection(),
            geotransform=covered_exposure_layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 24
0
    def run(self, layers):
        """Plugin for impact of population as derived by categorised hazard.

        :param layers: List of layers expected to contain

            * hazard_layer: Raster layer of categorised hazard
            * exposure_layer: Raster layer of population data

        Counts number of people exposed to each category of the hazard

        :returns:
          Map of population exposed to high category
          Table with number of people in each category
        """

        # The 3 category
        high_t = self.parameters['Categorical thresholds'][2]
        medium_t = self.parameters['Categorical thresholds'][1]
        low_t = self.parameters['Categorical thresholds'][0]

        # Identify hazard and exposure layers
        hazard_layer = get_hazard_layer(layers)  # Categorised Hazard
        exposure_layer = get_exposure_layer(layers)  # Population Raster

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

        # Extract data as numeric arrays
        C = hazard_layer.get_data(nan=0.0)  # Category

        # Calculate impact as population exposed to each category
        P = exposure_layer.get_data(nan=0.0, scaling=True)
        H = numpy.where(C <= high_t, P, 0)
        M = numpy.where(C < medium_t, P, 0)
        L = numpy.where(C < low_t, P, 0)

        # Count totals
        total = int(numpy.sum(P))
        high = int(numpy.sum(H)) - int(numpy.sum(M))
        medium = int(numpy.sum(M)) - int(numpy.sum(L))
        low = int(numpy.sum(L))
        total_impact = high + medium + low

        # Don't show digits less than a 1000
        total = population_rounding(total)
        total_impact = population_rounding(total_impact)
        high = population_rounding(high)
        medium = population_rounding(medium)
        low = population_rounding(low)

        minimum_needs = [
            parameter.serialize()
            for parameter in self.parameters['minimum needs']
        ]

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([tr('People impacted '),
                      '%s' % format_int(total_impact)],
                     header=True),
            TableRow(
                [tr('People in high hazard area '),
                 '%s' % format_int(high)],
                header=True),
            TableRow([
                tr('People in medium hazard area '),
                '%s' % format_int(medium)
            ],
                     header=True),
            TableRow([tr('People in low hazard area'),
                      '%s' % format_int(low)],
                     header=True)
        ]

        impact_table = Table(table_body).toNewlineFreeString()

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Map shows population count in high or medium hazard area'),
            tr('Total population: %s') % format_int(total),
            TableRow(
                tr('Table below shows the minimum needs for all '
                   'affected people'))
        ])

        total_needs = evacuated_population_needs(total_impact, minimum_needs)
        for frequency, needs in total_needs.items():
            table_body.append(
                TableRow([
                    tr('Needs should be provided %s' % frequency),
                    tr('Total')
                ],
                         header=True))
            for resource in needs:
                table_body.append(
                    TableRow([
                        tr(resource['table name']),
                        format_int(resource['amount'])
                    ]))

        impact_summary = Table(table_body).toNewlineFreeString()
        map_title = tr('People in high hazard areas')

        # Generate 8 equidistant classes across the range of flooded population
        # 8 is the number of classes in the predefined flood population style
        # as imported
        # noinspection PyTypeChecker
        classes = numpy.linspace(numpy.nanmin(M.flat[:]),
                                 numpy.nanmax(M.flat[:]), 8)

        # Modify labels in existing flood style to show quantities
        style_classes = style_info['style_classes']

        style_classes[1]['label'] = tr('Low [%i people/cell]') % classes[1]
        style_classes[4]['label'] = tr('Medium [%i people/cell]') % classes[4]
        style_classes[7]['label'] = tr('High [%i people/cell]') % classes[7]

        style_info['legend_title'] = tr('Population Count')

        # Create raster object and return
        raster_layer = Raster(M,
                              projection=hazard_layer.get_projection(),
                              geotransform=hazard_layer.get_geotransform(),
                              name=tr('Population which %s') %
                              (get_function_title(self).lower()),
                              keywords={
                                  'impact_summary': impact_summary,
                                  'impact_table': impact_table,
                                  'map_title': map_title,
                                  'total_needs': total_needs
                              },
                              style_info=style_info)
        return raster_layer
Exemplo n.º 25
0
    def run(self):
        """Plugin for impact of population as derived by classified hazard.

        Counts number of people exposed to each class of the hazard

        :returns: Map of population exposed to high class
            Table with number of people in each class
        """

        # 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 low hazard zone')] = int(
            numpy.nansum(low_hazard_population))
        self.affected_population[tr('Population in medium hazard zone')] = int(
            numpy.nansum(medium_hazard_population))
        self.affected_population[tr('Population in high hazard zone')] = int(
            numpy.nansum(high_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

        # 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]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(target_field=None,
                          style_classes=style_classes,
                          style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            data=affected_population,
            projection=self.exposure.layer.get_projection(),
            geotransform=self.exposure.layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 26
0
    def run(self, layers):
        """Plugin for impact of population as derived by classified hazard.

        Input
        :param layers: List of layers expected to contain

              * hazard_layer: Raster layer of classified hazard
              * exposure_layer: Raster layer of population data

        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
        """

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

        # Identify hazard and exposure layers
        hazard_layer = get_hazard_layer(layers)  # Classified Hazard
        exposure_layer = get_exposure_layer(layers)  # Population Raster

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

        # Extract data as numeric arrays
        data = hazard_layer.get_data(nan=0.0)  # Class

        # Calculate impact as population exposed to each class
        population = exposure_layer.get_data(nan=0.0, scaling=True)
        if high_t == 0:
            hi = numpy.where(0, population, 0)
        else:
            hi = numpy.where(data == high_t, population, 0)
        if medium_t == 0:
            med = numpy.where(0, population, 0)
        else:
            med = numpy.where(data == medium_t, population, 0)
        if low_t == 0:
            lo = numpy.where(0, population, 0)
        else:
            lo = numpy.where(data == low_t, population, 0)
        if high_t == 0:
            impact = numpy.where((data == low_t) + (data == medium_t),
                                 population, 0)
        elif medium_t == 0:
            impact = numpy.where((data == low_t) + (data == high_t),
                                 population, 0)
        elif low_t == 0:
            impact = numpy.where((data == medium_t) + (data == high_t),
                                 population, 0)
        else:
            impact = numpy.where(
                (data == low_t) + (data == medium_t) + (data == high_t),
                population, 0)

        # Count totals
        total = int(numpy.sum(population))
        high = int(numpy.sum(hi))
        medium = int(numpy.sum(med))
        low = int(numpy.sum(lo))
        total_impact = int(numpy.sum(impact))

        # Perform population rounding based on number of people
        no_impact = population_rounding(total - total_impact)
        total = population_rounding(total)
        total_impact = population_rounding(total_impact)
        high = population_rounding(high)
        medium = population_rounding(medium)
        low = population_rounding(low)

        minimum_needs = [
            parameter.serialize()
            for parameter in self.parameters['minimum needs']
        ]

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([
                tr('Total Population Affected '),
                '%s' % format_int(total_impact)
            ],
                     header=True),
            TableRow([
                tr('Population in High hazard class areas '),
                '%s' % format_int(high)
            ]),
            TableRow([
                tr('Population in Medium hazard class areas '),
                '%s' % format_int(medium)
            ]),
            TableRow([
                tr('Population in Low hazard class areas '),
                '%s' % format_int(low)
            ]),
            TableRow(
                [tr('Population Not Affected'),
                 '%s' % format_int(no_impact)]),
            TableRow(
                tr('Table below shows the minimum needs for all '
                   'evacuated people'))
        ]

        total_needs = evacuated_population_needs(total_impact, minimum_needs)
        for frequency, needs in total_needs.items():
            table_body.append(
                TableRow([
                    tr('Needs should be provided %s' % frequency),
                    tr('Total')
                ],
                         header=True))
            for resource in needs:
                table_body.append(
                    TableRow([
                        tr(resource['table name']),
                        format_int(resource['amount'])
                    ]))

        impact_table = Table(table_body).toNewlineFreeString()

        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(TableRow(tr('How will warnings be disseminated?')))
        table_body.append(TableRow(tr('How will we reach stranded people?')))
        table_body.append(TableRow(tr('Do we have enough relief items?')))
        table_body.append(
            TableRow(
                tr('If yes, where are they located and how will we distribute '
                   'them?')))
        table_body.append(
            TableRow(
                tr('If no, where can we obtain additional relief items from '
                   'and how will we transport them to here?')))

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Map shows the numbers of people in high, medium, and low '
               'hazard class areas'),
            tr('Total population: %s') % format_int(total)
        ])
        impact_summary = Table(table_body).toNewlineFreeString()

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(impact.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], 'Low')
            elif i == 4:
                label = create_label(interval_classes[i], 'Medium')
            elif i == 7:
                label = create_label(interval_classes[i], 'High')
            else:
                label = create_label(interval_classes[i])
            style_class['label'] = label
            style_class['quantity'] = classes[i]
            if i == 0:
                transparency = 30
            else:
                transparency = 30
            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('Population affected by each class')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(people per cell)')
        legend_title = tr('Number of People')

        # Create raster object and return
        raster_layer = Raster(impact,
                              projection=hazard_layer.get_projection(),
                              geotransform=hazard_layer.get_geotransform(),
                              name=tr('Population which %s') %
                              (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)
        return raster_layer
Exemplo n.º 27
0
def start(west,
          north,
          east,
          south,
          since,
          until=None,
          data_dir=None,
          population=None):

    bbox = (west, north, east, south)

    year, month, day = [int(x) for x in since.split('-')]
    since = datetime.date(year, month, day)

    if not isinstance(until, datetime.date):
        year, month, day = [int(x) for x in until.split('-')]
        until = datetime.date(year, month, day)
    else:
        until = until

    # Make sure the inputs are divisible by 10.
    for item in bbox:
        msg = "%d is not divisible by 10." % item
        assert int(item) % 10 == 0, msg

    the_viewports = viewports(bbox)
    the_timespan = timespan(since, until)

    data_dir = os.path.abspath(data_dir)

    if not os.path.exists(data_dir):
        os.mkdir(data_dir)

    print 'Downloading layers per day'
    # Download the layers for the given viewport and timespan.
    download(the_viewports, the_timespan, data_dir)

    print 'Merging layers per day'
    merged_files = merge(the_timespan, data_dir)

    flood_filename = os.path.join(data_dir, 'flood_severity.tif')

    if not os.path.exists(flood_filename):
        if len(merged_files) > 0:
            # Add all the pixels with a value higher than 3.
            #accumulate(merged_files, flood_filename, threshold=3)
            flooded = _flood_severity(merged_files)
            flooded.write_to_file(flood_filename)

            subprocess.call([
                'gdal_merge.py', '-co', 'compress=packbits', '-o',
                'flood_severity_compressed.tif', '-ot', 'Byte', flood_filename
            ],
                            stdout=open(os.devnull, 'w'))
            os.remove(flood_filename)
            os.rename('flood_severity_compressed.tif', flood_filename)
        else:
            raise Exception('No merged files found for %s' % the_timespan)

    population_file = os.path.join(data_dir, population)
    population_object = Raster(population_file)
    # get population bbox
    pop_bbox = population_object.get_bounding_box()

    # get resolutions and pick the best
    pop_resolution = population_object.get_resolution()[0]

    hazard_object = Raster(flood_filename)
    hazard_resolution = hazard_object.get_resolution()[0]
    hazard_bbox = hazard_object.get_bounding_box()

    if pop_bbox[0] > bbox[0] and pop_bbox[1] > bbox[1] and pop_bbox[2] < bbox[
            2] and pop_bbox[3] < bbox[3]:
        hazard_file = clip(flood_filename, pop_bbox, cellSize=pop_resolution)
        exposure_layer = population_file
    else:
        hazard_file = clip(flood_filename,
                           hazard_bbox,
                           cellSize=pop_resolution)
        exposure_layer = clip(population_file, hazard_bbox, cellSize=None)

    basename, ext = os.path.splitext(hazard_file)
    keywords_file = basename + '.keywords'

    if not os.path.exists(keywords_file):
        with open(keywords_file, 'w') as f:
            f.write(FLOOD_KEYWORDS)

    impact = calculate(hazard_file, exposure_layer)

    impact.write_to_file('impact.tif')

    count = impact.keywords['count']
    pretty_date = until.strftime('%a %d, %b %Y')
    print pretty_date, "|", "People affected: %s / %s" % (
        count, impact.keywords['total'])
Exemplo n.º 28
0
    def run(self):
        """Run volcano population evacuation Impact Function.

        Counts number of people exposed to volcano event.

        :returns: Map of population exposed to the volcano hazard zone.
            The returned dict will include a table with number of people
            evacuated and supplies required.
        :rtype: dict

        :raises:
            * Exception - When hazard layer is not vector layer
            * RadiiException - When radii are not valid (they need to be
                monotonically increasing)
        """

        # Parameters
        self.hazard_class_attribute = self.hazard.keyword('field')
        name_attribute = self.hazard.keyword('volcano_name_field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True

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

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

        features = self.hazard.layer.get_data()

        # Get names of volcanoes considered
        if name_attribute in self.hazard.layer.get_attribute_names():
            # Run through all polygons and get unique names
            for row in features:
                self.volcano_names.add(row[name_attribute])

        # 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_population = 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_population[vector_hazard_class['name']] = 0

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

        # Count affected population per polygon and total
        for row in interpolated_layer.get_data():
            # Get population at this location
            population = row[self.target_field]
            if not numpy.isnan(population):
                population = float(population)
                # Update population count for this hazard zone
                hazard_value = get_key_for_value(
                    row[self.hazard_class_attribute],
                    self.hazard_class_mapping)
                if not hazard_value:
                    hazard_value = self._not_affected_value
                self.affected_population[hazard_value] += population

        # Count totals
        self.total_population = int(
            numpy.nansum(self.exposure.layer.get_data()))
        self.unaffected_population = (
            self.total_population - self.total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]

        # check for zero impact
        if self.total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(
            covered_exposure_layer.get_data().flat[:], len(colours))
        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        impact_data = self.generate_data()

        extra_keywords = {
            'target_field': self.target_field,
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': self.total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Raster(
            data=covered_exposure_layer.get_data(),
            projection=covered_exposure_layer.get_projection(),
            geotransform=covered_exposure_layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info
        )

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 29
0
    def run(self, layers=None):
        """Plugin for impact of population as derived by classified hazard.

        Input
        :param layers: List of layers expected to contain

              * hazard_layer: Raster layer of classified hazard
              * exposure_layer: Raster layer of population data

        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(layers)

        # The 3 classes
        # TODO (3.2): shouldnt these be defined in keywords rather? TS
        low_class = self.parameters['low_hazard_class']
        medium_class = self.parameters['medium_hazard_class']
        high_class = self.parameters['high_hazard_class']

        # 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.')

        # Identify hazard and exposure layers
        hazard_layer = self.hazard  # Classified Hazard
        exposure_layer = self.exposure  # Population Raster

        # Extract data as numeric arrays
        hazard_data = hazard_layer.get_data(nan=True)  # Class
        no_data_warning = False
        if has_no_data(hazard_data):
            no_data_warning = True

        # Calculate impact as population exposed to each class
        population = 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
        total_population = int(numpy.nansum(population))
        total_high_population = int(numpy.nansum(high_hazard_population))
        total_medium_population = int(numpy.nansum(medium_hazard_population))
        total_low_population = int(numpy.nansum(low_hazard_population))
        total_affected = int(numpy.nansum(affected_population))
        total_not_affected = total_population - total_affected

        # check for zero impact
        if total_affected == 0:
            table_body = [
                self.question,
                TableRow(
                    [tr('People affected'),
                     '%s' % format_int(total_affected)],
                    header=True)
            ]
            message = Table(table_body).toNewlineFreeString()
            raise ZeroImpactException(message)

        minimum_needs = [
            parameter.serialize()
            for parameter in self.parameters['minimum needs']
        ]

        table_body, total_needs = self._tabulate(
            population_rounding(total_high_population),
            population_rounding(total_low_population),
            population_rounding(total_medium_population), minimum_needs,
            population_rounding(total_not_affected), self.question,
            population_rounding(total_affected))

        impact_table = Table(table_body).toNewlineFreeString()

        table_body = self._tabulate_action_checklist(
            table_body, population_rounding(total_population), no_data_warning)
        impact_summary = Table(table_body).toNewlineFreeString()

        # 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('Population affected by each class')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(people per cell)')
        legend_title = tr('Number of People')

        # Create raster object and return
        raster_layer = Raster(
            data=affected_population,
            projection=exposure_layer.get_projection(),
            geotransform=exposure_layer.get_geotransform(),
            name=tr('Population which %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
Exemplo n.º 30
0
    def run(self):
        """Plugin for impact of population as derived by classified hazard.

        Counts number of people exposed to each class of the hazard

        :returns: Map of population exposed to high class
            Table with number of people in each class
        """

        # 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 low hazard zone')] = int(
                numpy.nansum(low_hazard_population))
        self.affected_population[
            tr('Population in medium hazard zone')] = int(
                numpy.nansum(medium_hazard_population))
        self.affected_population[
            tr('Population in high hazard zone')] = int(
                numpy.nansum(high_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

        # 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]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(
            target_field=None,
            style_classes=style_classes,
            style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            data=affected_population,
            projection=self.exposure.layer.get_projection(),
            geotransform=self.exposure.layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 31
0
    def run(self, layers, x=0.62275231, y=8.03314466):  # , zeta=2.15):
        """Gender specific earthquake impact model

        Input
          layers: List of layers expected to contain
              H: Raster layer of MMI ground shaking
              P: Raster layer of population density

        """

        # Define percentages of people being displaced at each mmi level
        displacement_rate = {
            1: 0,
            2: 0,
            3: 0,
            4: 0,
            5: 0,
            6: 0,
            7: 0.1,
            8: 0.5,
            9: 0.75,
            10: 1.0
        }

        # Extract input layers
        intensity = get_hazard_layer(layers)
        population = get_exposure_layer(layers)

        question = get_question(intensity.get_name(), population.get_name(),
                                self)

        # Extract data grids
        H = intensity.get_data()  # Ground Shaking
        P = population.get_data()  # Population Density

        # Calculate population affected by each MMI level
        # FIXME (Ole): this range is 2-9. Should 10 be included?
        mmi_range = range(2, 10)
        number_of_exposed = {}
        number_of_fatalities = {}

        # Calculate fatality rates for observed Intensity values (H
        # based on ITB power model
        R = numpy.zeros(H.shape)
        for mmi in mmi_range:

            # Identify cells where MMI is in class i
            mask = (H > mmi - 0.5) * (H <= mmi + 0.5)

            # Count population affected by this shake level
            I = numpy.where(mask, P, 0)

            # Calculate expected number of fatalities per level
            fatality_rate = numpy.power(10.0, x * mmi - y)
            F = fatality_rate * I

            # Sum up fatalities to create map
            R += F

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = numpy.nansum(I.flat)
            number_of_fatalities[mmi] = numpy.nansum(F.flat)

        # Set resulting layer to zero when less than a threshold. This is to
        # achieve transparency (see issue #126).
        R[R < 1] = numpy.nan

        # Total statistics
        total = numpy.nansum(P.flat)

        # Compute number of fatalities
        fatalities = numpy.nansum(number_of_fatalities.values())

        # Compute number of people displaced due to building collapse
        displaced = 0
        for mmi in mmi_range:
            displaced += displacement_rate[mmi] * number_of_exposed[mmi]
        displaced_women = displaced * 0.52  # Could be made province dependent
        displaced_pregnant_women = displaced_women * 0.01387  # CHECK

        # Generate impact report
        table_body = [question]

        # Add total fatality estimate
        s = str(int(fatalities)).rjust(10)
        table_body.append(
            TableRow([tr('Number of fatalities'), s], header=True))

        # Add total estimate of people displaced
        s = str(int(displaced)).rjust(10)
        table_body.append(
            TableRow([tr('Number of people displaced'), s], header=True))
        s = str(int(displaced_women)).rjust(10)
        table_body.append(
            TableRow([tr('Number of women displaced'), s], header=True))
        s = str(int(displaced_pregnant_women)).rjust(10)
        table_body.append(
            TableRow([tr('Number of pregnant women displaced'), s],
                     header=True))

        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(
            tr('Are enough shelters available for %i women?') %
            displaced_women)
        table_body.append(
            tr('Are enough facilities available to assist %i '
               'pregnant women?') % displaced_pregnant_women)

        table_body.append(TableRow(tr('Notes'), header=True))

        table_body.append(
            tr('Fatality model is from '
               'Institute of Teknologi Bandung 2012.'))

        impact_summary = Table(table_body).toNewlineFreeString()
        impact_table = impact_summary
        map_title = tr('Earthquake impact to population')

        # Create new layer and return
        L = Raster(R,
                   projection=population.get_projection(),
                   geotransform=population.get_geotransform(),
                   keywords={
                       'impact_summary': impact_summary,
                       'total_population': total,
                       'total_fatalities': fatalities,
                       'impact_table': impact_table,
                       'map_title': map_title
                   },
                   name=tr('Estimated fatalities'),
                   style_info=earthquake_fatality_style)

        # Maybe return a shape file with contours instead
        return L
Exemplo n.º 32
0
class ITBFatalityFunction(FunctionProvider):
    """Indonesian Earthquake Fatality Model

    This model was developed by Institut Teknologi Bandung (ITB) and
    implemented by Dr. Hadi Ghasemi, Geoscience Australia.


    Reference:

    Indonesian Earthquake Building-Damage and Fatality Models and
    Post Disaster Survey Guidelines Development,
    Bali, 27-28 February 2012, 54pp.


    Algorithm:

    In this study, the same functional form as Allen (2009) is adopted
    to express fatality rate as a function of intensity (see Eq. 10 in the
    report). The Matlab built-in function (fminsearch) for  Nelder-Mead
    algorithm was used to estimate the model parameters. The objective
    function (L2G norm) that is minimised during the optimisation is the
    same as the one used by Jaiswal et al. (2010).

    The coefficients used in the indonesian model are
    x=0.62275231, y=8.03314466, zeta=2.15

    Allen, T. I., Wald, D. J., Earle, P. S., Marano, K. D., Hotovec, A. J.,
    Lin, K., and Hearne, M., 2009. An Atlas of ShakeMaps and population
    exposure catalog for earthquake loss modeling, Bull. Earthq. Eng. 7,
    701-718.

    Jaiswal, K., and Wald, D., 2010. An empirical model for global earthquake
    fatality estimation, Earthq. Spectra 26, 1017-1037.


    Caveats and limitations:

    The current model is the result of the above mentioned workshop and
    reflects the best available information. However, the current model
    has a number of issues listed below and is expected to evolve further
    over time.

    1 - The model is based on limited number of observed fatality
        rates during 4 past fatal events.
    2 - The model clearly over-predicts the fatality rates at
        intensities higher than VIII.
    3 - The model only estimates the expected fatality rate for a given
        intensity level; however the associated uncertainty for the proposed
        model is not addressed.
    4 - There are few known mistakes in developing the current model:
        - rounding MMI values to the nearest 0.5,
        - Implementing Finite-Fault models of candidate events, and
        - consistency between selected GMPEs with those in use by BMKG.
          These issues will be addressed by ITB team in the final report.

    Note: Because of these caveats, decisions should not be made solely on
    the information presented here and should always be verified by ground
    truthing and other reliable information sources.

    :author Hadi Ghasemi
    :rating 3

    :param requires category=='hazard' and \
                    subcategory=='earthquake' and \
                    layertype=='raster' and \
                    unit=='MMI'

    :param requires category=='exposure' and \
                    subcategory=='population' and \
                    layertype=='raster'

    """

    title = tr('Die or be displaced')
    synopsis = tr(
        'To asses the impact of earthquake on population based on earthquake '
        'model developed by ITB')
    citations = tr(
        ' * Indonesian Earthquake Building-Damage and Fatality Models and '
        '   Post Disaster Survey Guidelines Development Bali, 27-28 '
        '   February 2012, 54pp.\n'
        ' * Allen, T. I., Wald, D. J., Earle, P. S., Marano, K. D., '
        '   Hotovec, A. J., Lin, K., and Hearne, M., 2009. An Atlas '
        '   of ShakeMaps and population exposure catalog for '
        '   earthquake loss modeling, Bull. Earthq. Eng. 7, 701-718.\n'
        ' * Jaiswal, K., and Wald, D., 2010. An empirical model for '
        '   global earthquake fatality estimation, Earthq. Spectra '
        '   26, 1017-1037.\n')
    limitation = tr(
        ' - The model is based on limited number of observed fatality '
        '   rates during 4 past fatal events. \n'
        ' - The model clearly over-predicts the fatality rates at '
        '   intensities higher than VIII.\n'
        ' - The model only estimates the expected fatality rate '
        '   for a given intensity level; however the associated '
        '   uncertainty for the proposed model is not addressed.\n'
        ' - There are few known mistakes in developing the current '
        '   model:\n\n'
        '   * rounding MMI values to the nearest 0.5,\n'
        '   * Implementing Finite-Fault models of candidate events, and\n'
        '   * consistency between selected GMPEs with those in use by '
        '     BMKG.\n')
    actions = tr(
        'Provide details about the population will be die or displaced')
    detailed_description = tr(
        'This model was developed by Institut Teknologi Bandung (ITB) '
        'and implemented by Dr. Hadi Ghasemi, Geoscience Australia\n'
        'Algorithm:\n'
        'In this study, the same functional form as Allen (2009) is '
        'adopted o express fatality rate as a function of intensity '
        '(see Eq. 10 in the report). The Matlab built-in function '
        '(fminsearch) for  Nelder-Mead algorithm was used to estimate '
        'the model parameters. The objective function (L2G norm) that '
        'is minimized during the optimisation is the same as the one '
        'used by Jaiswal et al. (2010).\n'
        'The coefficients used in the indonesian model are x=0.62275231, '
        'y=8.03314466, zeta=2.15')
    defaults = get_defaults()

    parameters = OrderedDict([
        ('x', 0.62275231),
        ('y', 8.03314466),  # Model coefficients
        # Rates of people displaced for each MMI level
        ('displacement_rate', {
            1: 0,
            2: 0,
            3: 0,
            4: 0,
            5: 0,
            6: 1.0,
            7: 1.0,
            8: 1.0,
            9: 1.0,
            10: 1.0
        }),
        ('mmi_range', range(2, 10)),
        ('step', 0.5),
        # Threshold below which layer should be transparent
        ('tolerance', 0.01),
        ('calculate_displaced_people', True),
        ('postprocessors',
         OrderedDict([
             ('Gender', {
                 'on': True
             }),
             ('Age', {
                 'on':
                 True,
                 'params':
                 OrderedDict([('youth_ratio', defaults['YOUTH_RATIO']),
                              ('adult_ratio', defaults['ADULT_RATIO']),
                              ('elder_ratio', defaults['ELDER_RATIO'])])
             }), ('MinimumNeeds', {
                 'on': True
             })
         ])),
        ('minimum needs', default_minimum_needs())
    ])

    def fatality_rate(self, mmi):
        """
        ITB method to compute fatality rate
        :param mmi:
        """
        # As per email discussion with Ole, Trevor, Hadi, mmi < 4 will have
        # a fatality rate of 0 - Tim
        if mmi < 4:
            return 0

        x = self.parameters['x']
        y = self.parameters['y']
        return numpy.power(10.0, x * mmi - y)

    def run(self, layers):
        """Indonesian Earthquake Fatality Model

        Input:

        :param layers: List of layers expected to contain,

                my_hazard: Raster layer of MMI ground shaking

                my_exposure: Raster layer of population density
        """

        displacement_rate = self.parameters['displacement_rate']

        # Tolerance for transparency
        tolerance = self.parameters['tolerance']

        # Extract input layers
        intensity = get_hazard_layer(layers)
        population = get_exposure_layer(layers)

        question = get_question(intensity.get_name(), population.get_name(),
                                self)

        # Extract data grids
        my_hazard = intensity.get_data()  # Ground Shaking
        my_exposure = population.get_data(scaling=True)  # Population Density

        # Calculate population affected by each MMI level
        # FIXME (Ole): this range is 2-9. Should 10 be included?

        mmi_range = self.parameters['mmi_range']
        number_of_exposed = {}
        number_of_displaced = {}
        number_of_fatalities = {}

        # Calculate fatality rates for observed Intensity values (my_hazard
        # based on ITB power model
        R = numpy.zeros(my_hazard.shape)
        for mmi in mmi_range:
            # Identify cells where MMI is in class i and
            # count population affected by this shake level
            I = numpy.where((my_hazard > mmi - self.parameters['step']) *
                            (my_hazard <= mmi + self.parameters['step']),
                            my_exposure, 0)

            # Calculate expected number of fatalities per level
            fatality_rate = self.fatality_rate(mmi)

            F = fatality_rate * I

            # Calculate expected number of displaced people per level
            try:
                D = displacement_rate[mmi] * I
            except KeyError, e:
                msg = 'mmi = %i, I = %s, Error msg: %s' % (mmi, str(I), str(e))
                # noinspection PyExceptionInherit
                raise InaSAFEError(msg)

            # Adjust displaced people to disregard fatalities.
            # Set to zero if there are more fatalities than displaced.
            D = numpy.where(D > F, D - F, 0)

            # Sum up numbers for map
            R += D  # Displaced

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = numpy.nansum(I.flat)
            number_of_displaced[mmi] = numpy.nansum(D.flat)
            # noinspection PyUnresolvedReferences
            number_of_fatalities[mmi] = numpy.nansum(F.flat)

        # Set resulting layer to NaN when less than a threshold. This is to
        # achieve transparency (see issue #126).
        R[R < tolerance] = numpy.nan

        # Total statistics
        total = int(round(numpy.nansum(my_exposure.flat) / 1000) * 1000)

        # Compute number of fatalities
        fatalities = int(
            round(numpy.nansum(number_of_fatalities.values()) / 1000)) * 1000
        # As per email discussion with Ole, Trevor, Hadi, total fatalities < 50
        # will be rounded down to 0 - Tim
        if fatalities < 50:
            fatalities = 0

        # Compute number of people displaced due to building collapse
        displaced = int(
            round(numpy.nansum(number_of_displaced.values()) / 1000)) * 1000

        # Generate impact report
        table_body = [question]

        # Add total fatality estimate
        s = format_int(fatalities)
        table_body.append(
            TableRow([tr('Number of fatalities'), s], header=True))

        if self.parameters['calculate_displaced_people']:
            # Add total estimate of people displaced
            s = format_int(displaced)
            table_body.append(
                TableRow([tr('Number of people displaced'), s], header=True))
        else:
            displaced = 0

        # Add estimate of total population in area
        s = format_int(int(total))
        table_body.append(
            TableRow([tr('Total number of people'), s], header=True))

        # Calculate estimated needs based on BNPB Perka 7/2008 minimum bantuan
        # FIXME: Refactor and share
        minimum_needs = self.parameters['minimum needs']
        needs = evacuated_population_weekly_needs(displaced, minimum_needs)

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([tr('Fatalities'),
                      '%s' % format_int(fatalities)],
                     header=True),
            TableRow([tr('People displaced'),
                      '%s' % format_int(displaced)],
                     header=True),
            TableRow(
                tr('Map shows density estimate of '
                   'displaced population')),
            TableRow([tr('Needs per week'), tr('Total')], header=True),
            [tr('Rice [kg]'), format_int(needs['rice'])],
            [tr('Drinking Water [l]'),
             format_int(needs['drinking_water'])],
            [tr('Clean Water [l]'),
             format_int(needs['water'])],
            [tr('Family Kits'),
             format_int(needs['family_kits'])],
            TableRow(tr('Action Checklist:'), header=True)
        ]

        if fatalities > 0:
            table_body.append(
                tr('Are there enough victim identification '
                   'units available for %s people?') % format_int(fatalities))
        if displaced > 0:
            table_body.append(
                tr('Are there enough shelters and relief items '
                   'available for %s people?') % format_int(displaced))
            table_body.append(
                TableRow(
                    tr('If yes, where are they located and '
                       'how will we distribute them?')))
            table_body.append(
                TableRow(
                    tr('If no, where can we obtain '
                       'additional relief items from and '
                       'how will we transport them?')))

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Total population: %s') % format_int(total),
            tr('People are considered to be displaced if '
               'they experience and survive a shake level'
               'of more than 5 on the MMI scale '),
            tr('Minimum needs are defined in BNPB '
               'regulation 7/2008'),
            tr('The fatality calculation assumes that '
               'no fatalities occur for shake levels below 4 '
               'and fatality counts of less than 50 are '
               'disregarded.'),
            tr('All values are rounded up to the nearest '
               'integer in order to avoid representing human '
               'lives as fractionals.')
        ])

        table_body.append(TableRow(tr('Notes'), header=True))
        table_body.append(
            tr('Fatality model is from '
               'Institute of Teknologi Bandung 2012.'))
        table_body.append(tr('Population numbers rounded to nearest 1000.'))

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

        # check for zero impact
        if numpy.nanmax(R) == 0 == numpy.nanmin(R):
            table_body = [
                question,
                TableRow([tr('Fatalities'),
                          '%s' % format_int(fatalities)],
                         header=True)
            ]
            my_message = Table(table_body).toNewlineFreeString()
            raise ZeroImpactException(my_message)

        # Create style
        colours = ['#EEFFEE', '#FFFF7F', '#E15500', '#E4001B', '#730000']
        classes = create_classes(R.flat[:], len(colours))
        interval_classes = humanize_class(classes)
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            style_class['quantity'] = classes[i]
            if i == 0:
                transparency = 100
            else:
                transparency = 30
            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('Earthquake impact to population')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(people per cell)')
        legend_title = tr('Population density')

        # Create raster object and return
        L = Raster(R,
                   projection=population.get_projection(),
                   geotransform=population.get_geotransform(),
                   keywords={
                       'impact_summary': impact_summary,
                       'total_population': total,
                       'total_fatalities': fatalities,
                       'fatalites_per_mmi': number_of_fatalities,
                       'exposed_per_mmi': number_of_exposed,
                       'displaced_per_mmi': number_of_displaced,
                       'impact_table': impact_table,
                       'map_title': map_title,
                       'legend_notes': legend_notes,
                       'legend_units': legend_units,
                       'legend_title': legend_title
                   },
                   name=tr('Estimated displaced population per cell'),
                   style_info=style_info)

        return L
Exemplo n.º 33
0
    def run(self):
        """Run volcano point population evacuation Impact Function.

        Counts number of people exposed to volcano event.

        :returns: Map of population exposed to the volcano hazard zone.
            The returned dict will include a table with number of people
            evacuated and supplies required.
        :rtype: dict

        :raises:
            * Exception - When hazard layer is not vector layer
            * RadiiException - When radii are not valid (they need to be
                monotonically increasing)
        """
        self.validate()
        self.prepare()

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

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

        # Get parameters from layer's keywords
        volcano_name_attribute = self.hazard.keyword('volcano_name_field')

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

        data_table = self.hazard.layer.get_data()

        # Use concentric circles
        category_title = 'Radius'

        centers = self.hazard.layer.get_geometry()
        hazard_layer = buffer_points(centers,
                                     radii,
                                     category_title,
                                     data_table=data_table)

        # Get names of volcanoes considered
        if volcano_name_attribute in hazard_layer.get_attribute_names():
            volcano_name_list = []
            # Run through all polygons and get unique names
            for row in data_table:
                volcano_name_list.append(row[volcano_name_attribute])

            volcano_names = ''
            for radius in volcano_name_list:
                volcano_names += '%s, ' % radius
            self.volcano_names = volcano_names[:-2]  # Strip trailing ', '

        # Run interpolation function for polygon2raster
        interpolated_layer, covered_exposure_layer = \
            assign_hazard_values_to_exposure_data(
                hazard_layer,
                self.exposure.layer,
                attribute_name=self.target_field
            )

        # Initialise affected population per categories
        for radius in radii:
            category = 'Radius %s km ' % format_int(radius)
            self.affected_population[category] = 0

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True
        # Count affected population per polygon and total
        for row in interpolated_layer.get_data():
            # Get population at this location
            population = row[self.target_field]
            if not numpy.isnan(population):
                population = float(population)
                # Update population count for this category
                category = 'Radius %s km ' % format_int(row[category_title])
                self.affected_population[category] += population

        # Count totals
        self.total_population = population_rounding(
            int(numpy.nansum(self.exposure.layer.get_data())))

        self.minimum_needs = [
            parameter.serialize() for parameter in filter_needs_parameters(
                self.parameters['minimum needs'])
        ]

        impact_table = impact_summary = self.html_report()

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(covered_exposure_layer.get_data().flat[:],
                                 len(colours))
        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        # For printing map purpose
        map_title = tr('People affected by the buffered point volcano')
        legend_title = tr('Population')
        legend_units = tr('(people per cell)')
        legend_notes = tr('Thousand separator is represented by  %s' %
                          get_thousand_separator())

        # Create vector layer and return
        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,
            'total_needs': self.total_needs
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        impact_layer = Raster(
            data=covered_exposure_layer.get_data(),
            projection=covered_exposure_layer.get_projection(),
            geotransform=covered_exposure_layer.get_geotransform(),
            name=tr('People affected by the buffered point volcano'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
Exemplo n.º 34
0
def convert_netcdf2tif(filename, n):
    """Convert netcdf to tif aggregating first n bands

    Args
        * filename: NetCDF multiband raster with extension .nc
        * n: Positive integer determining how many bands to use

    Returns
        * Raster file in tif format. Each pixel will be the maximum
          of that pixel in the first n bands in the input file.

    """

    if not isinstance(filename, basestring):
        msg = 'Argument filename should be a string. I got %s' % filename
        raise RuntimeError(msg)

    basename, ext = os.path.splitext(filename)
    msg = ('Expected NetCDF file with extension .nc - '
           'Instead I got %s' % filename)
    if ext != '.nc':
        raise RuntimeError(msg)

    try:
        n = int(n)
    except:
        msg = 'Argument N should be an integer. I got %s' % n
        raise RuntimeError(msg)

    print filename, n

    # Read NetCDF file
    fid = NetCDFFile(filename)
    dimensions = fid.dimensions.keys()
    variables = fid.variables.keys()

    title = getattr(fid, 'title')
    institution = getattr(fid, 'institution')
    source = getattr(fid, 'source')
    history = getattr(fid, 'history')
    references = getattr(fid, 'references')
    conventions = getattr(fid, 'Conventions')
    coordinate_system = getattr(fid, 'coordinate_system')

    print 'Read from %s' % filename
    print 'Title: %s' % title
    print 'Institution: %s' % institution
    print 'Source: %s' % source
    print 'History: %s' % history
    print 'References: %s' % references
    print 'Conventions: %s' % conventions
    print 'Coordinate system: %s' % coordinate_system

    print 'Dimensions: %s' % dimensions
    print 'Variables:  %s' % variables

    # Get data
    x = fid.variables['x'][:]
    y = fid.variables['y'][:]
    t = fid.variables['time'][:]
    inundation_depth = fid.variables['Inundation_Depth'][:]

    T = inundation_depth.shape[0]  # Number of time steps
    M = inundation_depth.shape[1]  # Steps in the y direction
    N = inundation_depth.shape[2]  # Steps in the x direction

    # Compute the max of the first n timesteps
    A = numpy.zeros((M, N), dtype='float')
    for i in range(n):
        B = inundation_depth[i, :, :]
        A = numpy.maximum(A, B)

    geotransform = raster_geometry2geotransform(x, y)
    print 'Geotransform', geotransform

    # Write result to tif file
    # NOTE: This assumes a default projection (WGS 84, geographic)
    date = os.path.split(basename)[-1].split('_')[0]
    print 'date', date
    R = Raster(data=A,
               geotransform=geotransform,
               keywords={'category': 'hazard',
                         'subcategory': 'flood',
                         'unit': 'm',
                         'title': ('%d hour flood forecast '
                                   'in Jakarta at %s' % (n, date))})

    tif_filename = '%s_%d_hours.tif' % (basename, n)
    R.write_to_file(tif_filename)

    print 'Success: %d hour forecast written to %s' % (n, R.filename)
    return tif_filename
Exemplo n.º 35
0
    def run(layers):
        """Risk plugin for earthquake fatalities

        Input
          layers: List of layers expected to contain
              H: Raster layer of flood depth
              P: Raster layer of population data on the same grid as H
        """

        # Depth above which people are regarded affected [m]
        threshold = 0.1
        # Identify hazard and exposure layers
        inundation = get_hazard_layer(layers)  # Flood inundation [m]

        # Get population and gender ratio
        population = gender_ratio = None
        for layer in get_exposure_layers(layers):
            keywords = layer.get_keywords()

            if 'datatype' not in keywords:
                population = layer
            else:
                datatype = keywords['datatype']

                if 'ratio' not in datatype:
                    population = layer
                else:
                    # if 'female' in datatype and 'ratio' in datatype:
                    gender_ratio_unit = keywords['unit']

                    msg = ('Unit for gender ratio must be either '
                           '"percent" or "ratio"')
                    if gender_ratio_unit not in ['percent', 'ratio']:
                        raise Exception(msg)

                    gender_ratio = layer

        msg = 'No population layer was found in: %s' % str(layers)
        verify(population is not None, msg)

        # Extract data as numeric arrays
        D = inundation.get_data(nan=0.0)  # Depth

        # Calculate impact as population exposed to depths > threshold
        if population.get_resolution(native=True, isotropic=True) < 0.0005:
            # Keep this for backwards compatibility just a little while
            # This uses the original custom population set and
            # serves as a reference

            P = population.get_data(nan=0.0)  # Population density
            pixel_area = 2500
            I = numpy.where(D > threshold, P, 0) / 100000.0 * pixel_area
        else:
            # This is the new generic way of scaling (issue #168 and #172)
            P = population.get_data(nan=0.0, scaling=True)
            I = numpy.where(D > threshold, P, 0)

        if gender_ratio is not None:
            # Extract gender ratio at each pixel (as ratio)
            G = gender_ratio.get_data(nan=0.0)
            if gender_ratio_unit == 'percent':
                G /= 100

            # Calculate breakdown
            P_female = P * G
            P_male = P - P_female

            I_female = I * G
            I_male = I - I_female

        # Generate text with result for this study
        total = format_int(int(nansum(P.flat) / 1000))
        count = format_int(int(nansum(I.flat) / 1000))

        # Create report
        impact_summary = ('<table border="0" width="320px">'
                          '   <tr><td><b>%s&#58;</b></td>'
                          '<td align="right"><b>%s</b></td></tr>' %
                          ('Jumlah Penduduk', total))
        if gender_ratio is not None:
            total_female = format_int(int(nansum(P_female.flat) / 1000))
            total_male = format_int(int(nansum(P_male.flat) / 1000))

            impact_summary += ('        <tr><td>%s&#58;</td>'
                               '<td align="right">%s</td></tr>' %
                               (' - Wanita', total_female))
            impact_summary += ('        <tr><td>%s&#58;</td>'
                               '<td align="right">%s</td></tr>' %
                               (' - Pria', total_male))
            impact_summary += '<tr><td>&nbsp;</td></tr>'  # Blank row

        impact_summary += (
            '   <tr><td><b>%s&#58;</b></td>'
            '<td align="right"><b>%s</b></td></tr>' %
            ('Perkiraan Jumlah Terdampak (> %.1fm)' % threshold, count))

        if gender_ratio is not None:
            affected_female = format_int(int(nansum(I_female.flat) / 1000))
            affected_male = format_int(int(nansum(I_male.flat) / 1000))

            impact_summary += ('        <tr><td>%s&#58;</td>'
                               '<td align="right">%s</td></tr>' %
                               (' - Wanita', affected_female))
            impact_summary += ('        <tr><td>%s&#58;</td>'
                               '<td align="right">%s</td></tr>' %
                               (' - Pria', affected_male))

        impact_summary += '</table>'

        impact_summary += '<br>'  # Blank separation row
        impact_summary += 'Catatan&#58; Semua nomor x 1000'

        # Create raster object and return
        R = Raster(I,
                   projection=inundation.get_projection(),
                   geotransform=inundation.get_geotransform(),
                   name='People affected',
                   keywords={'impact_summary': impact_summary})
        return R
Exemplo n.º 36
0
def convert_netcdf2tif(filename, n):
    """Convert netcdf to tif aggregating first n bands

    Args
        * filename: NetCDF multiband raster with extension .nc
        * n: Positive integer determining how many bands to use

    Returns
        * Raster file in tif format. Each pixel will be the maximum
          of that pixel in the first n bands in the input file.

    """

    if not isinstance(filename, basestring):
        msg = "Argument filename should be a string. I got %s" % filename
        raise RuntimeError(msg)

    basename, ext = os.path.splitext(filename)
    msg = "Expected NetCDF file with extension .nc - " "Instead I got %s" % filename
    if ext != ".nc":
        raise RuntimeError(msg)

    try:
        n = int(n)
    except:
        msg = "Argument N should be an integer. I got %s" % n
        raise RuntimeError(msg)

    print filename, n

    # Read NetCDF file
    fid = NetCDFFile(filename)
    dimensions = fid.dimensions.keys()
    variables = fid.variables.keys()

    title = getattr(fid, "title")
    institution = getattr(fid, "institution")
    source = getattr(fid, "source")
    history = getattr(fid, "history")
    references = getattr(fid, "references")
    conventions = getattr(fid, "Conventions")
    coordinate_system = getattr(fid, "coordinate_system")

    print "Read from %s" % filename
    print "Title: %s" % title
    print "Institution: %s" % institution
    print "Source: %s" % source
    print "History: %s" % history
    print "References: %s" % references
    print "Conventions: %s" % conventions
    print "Coordinate system: %s" % coordinate_system

    print "Dimensions: %s" % dimensions
    print "Variables:  %s" % variables

    # Get data
    x = fid.variables["x"][:]
    y = fid.variables["y"][:]
    t = fid.variables["time"][:]
    inundation_depth = fid.variables["Inundation_Depth"][:]

    T = inundation_depth.shape[0]  # Number of time steps
    M = inundation_depth.shape[1]  # Steps in the y direction
    N = inundation_depth.shape[2]  # Steps in the x direction

    # Compute the max of the first n timesteps
    A = numpy.zeros((M, N), dtype="float")
    for i in range(n):
        B = inundation_depth[i, :, :]
        A = numpy.maximum(A, B)

    geotransform = raster_geometry2geotransform(x, y)
    print "Geotransform", geotransform

    # Write result to tif file
    # NOTE: This assumes a default projection (WGS 84, geographic)
    date = os.path.split(basename)[-1].split("_")[0]
    print "date", date
    R = Raster(
        data=A,
        geotransform=geotransform,
        keywords={
            "category": "hazard",
            "subcategory": "flood",
            "unit": "m",
            "title": ("%d hour flood forecast grid " "in Jakarta at %s" % (n, date)),
        },
    )

    tif_filename = "%s_%d_hours.tif" % (basename, n)
    R.write_to_file(tif_filename)

    print "Success: %d hour forecast written to %s" % (n, R.filename)
    return tif_filename
    def run(self, layers):
        """Risk plugin for flood population evacuation

        Input
          layers: List of layers expected to contain
              my_hazard: Raster layer of flood depth
              my_exposure: Raster layer of population data on the same grid
              as my_hazard

        Counts number of people exposed to flood levels exceeding
        specified threshold.

        Return
          Map of population exposed to flood levels exceeding the threshold
          Table with number of people evacuated and supplies required
        """

        # Identify hazard and exposure layers
        my_hazard = get_hazard_layer(layers)  # Flood inundation [m]
        my_exposure = get_exposure_layer(layers)

        question = get_question(my_hazard.get_name(), my_exposure.get_name(),
                                self)

        # Determine depths above which people are regarded affected [m]
        # Use thresholds from inundation layer if specified
        thresholds = self.parameters['thresholds [m]']

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

        # Extract data as numeric arrays
        D = my_hazard.get_data(nan=0.0)  # Depth

        # Calculate impact as population exposed to depths > max threshold
        P = my_exposure.get_data(nan=0.0, scaling=True)

        # Calculate impact to intermediate thresholds
        counts = []
        # merely initialize
        my_impact = None
        for i, lo in enumerate(thresholds):
            if i == len(thresholds) - 1:
                # The last threshold
                my_impact = M = numpy.where(D >= lo, P, 0)
            else:
                # Intermediate thresholds
                hi = thresholds[i + 1]
                M = numpy.where((D >= lo) * (D < hi), P, 0)

            # Count
            val = int(numpy.sum(M))

            # Don't show digits less than a 1000
            val = round_thousand(val)
            counts.append(val)

        # Count totals
        evacuated = counts[-1]
        total = int(numpy.sum(P))
        # Don't show digits less than a 1000
        total = round_thousand(total)

        # Calculate estimated minimum needs
        # The default value of each logistic is based on BNPB Perka 7/2008
        # minimum bantuan
        minimum_needs = self.parameters['minimum needs']
        mn_rice = minimum_needs['Rice']
        mn_drinking_water = minimum_needs['Drinking Water']
        mn_water = minimum_needs['Water']
        mn_family_kits = minimum_needs['Family Kits']
        mn_toilets = minimum_needs['Toilets']

        rice = int(evacuated * mn_rice)
        drinking_water = int(evacuated * mn_drinking_water)
        water = int(evacuated * mn_water)
        family_kits = int(evacuated * mn_family_kits)
        toilets = int(evacuated * mn_toilets)

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([(tr('People in %.1f m of water') % thresholds[-1]),
                      '%s*' % format_int(evacuated)],
                     header=True),
            TableRow(tr('* Number is rounded to the nearest 1000'),
                     header=False),
            TableRow(tr('Map shows population density needing evacuation')),
            TableRow([tr('Needs per week'), tr('Total')], header=True),
            [tr('Rice [kg]'), format_int(rice)],
            [tr('Drinking Water [l]'),
             format_int(drinking_water)],
            [tr('Clean Water [l]'), format_int(water)],
            [tr('Family Kits'), format_int(family_kits)],
            [tr('Toilets'), format_int(toilets)]
        ]

        table_body.append(TableRow(tr('Action Checklist:'), header=True))
        table_body.append(TableRow(tr('How will warnings be disseminated?')))
        table_body.append(TableRow(tr('How will we reach stranded people?')))
        table_body.append(TableRow(tr('Do we have enough relief items?')))
        table_body.append(
            TableRow(
                tr('If yes, where are they located and how '
                   'will we distribute them?')))
        table_body.append(
            TableRow(
                tr('If no, where can we obtain additional relief items from and how '
                   'will we transport them to here?')))

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Total population: %s') % format_int(total),
            tr('People need evacuation if flood levels exceed %(eps).1f m') % {
                'eps': thresholds[-1]
            },
            tr('Minimum needs are defined in BNPB regulation 7/2008'),
            tr('All values are rounded up to the nearest integer in order to '
               'avoid representing human lives as fractionals.')
        ])

        if len(counts) > 1:
            table_body.append(TableRow(tr('Detailed breakdown'), header=True))

            for i, val in enumerate(counts[:-1]):
                s = (tr('People in %(lo).1f m to %(hi).1f m of water: %(val)i')
                     % {
                         'lo': thresholds[i],
                         'hi': thresholds[i + 1],
                         'val': format_int(val)
                     })
                table_body.append(TableRow(s, header=False))

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

        # check for zero impact
        if numpy.nanmax(my_impact) == 0 == numpy.nanmin(my_impact):
            table_body = [
                question,
                TableRow([(tr('People in %.1f m of water') % thresholds[-1]),
                          '%s' % format_int(evacuated)],
                         header=True)
            ]
            my_message = Table(table_body).toNewlineFreeString()
            raise ZeroImpactException(my_message)

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(my_impact.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], 'Low')
            elif i == 4:
                label = create_label(interval_classes[i], 'Medium')
            elif i == 7:
                label = create_label(interval_classes[i], 'High')
            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('People in need of evacuation')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())
        legend_units = tr('(people per cell)')
        legend_title = tr('Population density')

        # Create raster object and return
        R = Raster(my_impact,
                   projection=my_hazard.get_projection(),
                   geotransform=my_hazard.get_geotransform(),
                   name=tr('Population which %s') % get_function_title(self),
                   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
                   },
                   style_info=style_info)
        return R
Exemplo n.º 38
0
    def run(self):
        """Run volcano population evacuation Impact Function.

        Counts number of people exposed to volcano event.

        :returns: Map of population exposed to the volcano hazard zone.
            The returned dict will include a table with number of people
            evacuated and supplies required.
        :rtype: dict

        :raises:
            * Exception - When hazard layer is not vector layer
            * RadiiException - When radii are not valid (they need to be
                monotonically increasing)
        """
        self.validate()
        self.prepare()

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

        # Parameters
        self.hazard_class_attribute = self.hazard.keyword('field')
        name_attribute = self.hazard.keyword('volcano_name_field')
        self.hazard_class_mapping = self.hazard.keyword('value_map')

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True

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

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

        features = self.hazard.layer.get_data()

        # Get names of volcanoes considered
        if name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = []
            # Run through all polygons and get unique names
            for row in features:
                volcano_name_list.append(row[name_attribute])

            self.volcano_names = ', '.join(set(volcano_name_list))

        # 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_population = 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_population[vector_hazard_class['name']] = 0

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

        # Count affected population per polygon and total
        for row in interpolated_layer.get_data():
            # Get population at this location
            population = row[self.target_field]
            if not numpy.isnan(population):
                population = float(population)
                # Update population count for this hazard zone
                hazard_value = get_key_for_value(
                    row[self.hazard_class_attribute],
                    self.hazard_class_mapping)
                if not hazard_value:
                    hazard_value = self._not_affected_value
                self.affected_population[hazard_value] += population

        # Count totals
        self.total_population = int(
            numpy.nansum(self.exposure.layer.get_data()))
        self.unaffected_population = (self.total_population -
                                      self.total_affected_population)

        self.minimum_needs = [
            parameter.serialize() for parameter in filter_needs_parameters(
                self.parameters['minimum needs'])
        ]

        impact_table = impact_summary = self.html_report()

        # check for zero impact
        if self.total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(covered_exposure_layer.get_data().flat[:],
                                 len(colours))
        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        # For printing map purpose
        map_title = tr('People affected by Volcano Hazard Zones')
        legend_title = tr('Population')
        legend_units = tr('(people per cell)')
        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,
            'total_needs': self.total_needs
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create vector layer and return
        impact_layer = Raster(
            data=covered_exposure_layer.get_data(),
            projection=covered_exposure_layer.get_projection(),
            geotransform=covered_exposure_layer.get_geotransform(),
            name=tr('People affected by volcano hazard zones'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        self._impact = impact_layer
        return impact_layer
    def run(self, layers):
        """Plugin for impact of population as derived by categorised hazard

        Input
          layers: List of layers expected to contain
              my_hazard: Raster layer of categorised hazard
              my_exposure: Raster layer of population data

        Counts number of people exposed to each category of the hazard

        Return
          Map of population exposed to high category
          Table with number of people in each category
        """

        # The 3 category
        high_t = 1
        medium_t = 0.66
        low_t = 0.34

        # Identify hazard and exposure layers
        my_hazard = get_hazard_layer(layers)  # Categorised Hazard
        my_exposure = get_exposure_layer(layers)  # Population Raster

        question = get_question(my_hazard.get_name(), my_exposure.get_name(),
                                self)

        # Extract data as numeric arrays
        C = my_hazard.get_data(nan=0.0)  # Category

        # Calculate impact as population exposed to each category
        P = my_exposure.get_data(nan=0.0, scaling=True)
        H = numpy.where(C == high_t, P, 0)
        M = numpy.where(C > medium_t, P, 0)
        L = numpy.where(C < low_t, P, 0)

        # Count totals
        total = int(numpy.sum(P))
        high = int(numpy.sum(H))
        medium = int(numpy.sum(M)) - int(numpy.sum(H))
        low = int(numpy.sum(L)) - int(numpy.sum(M))
        total_impact = high + medium + low

        # Don't show digits less than a 1000
        total = round_thousand(total)
        total_impact = round_thousand(total_impact)
        high = round_thousand(high)
        medium = round_thousand(medium)
        low = round_thousand(low)

        # Generate impact report for the pdf map
        table_body = [
            question,
            TableRow([tr('People impacted '),
                      '%s' % format_int(total_impact)],
                     header=True),
            TableRow(
                [tr('People in high hazard area '),
                 '%s' % format_int(high)],
                header=True),
            TableRow([
                tr('People in medium hazard area '),
                '%s' % format_int(medium)
            ],
                     header=True),
            TableRow([tr('People in low hazard area'),
                      '%s' % format_int(low)],
                     header=True)
        ]

        impact_table = Table(table_body).toNewlineFreeString()

        # Extend impact report for on-screen display
        table_body.extend([
            TableRow(tr('Notes'), header=True),
            tr('Map shows population density in high or medium '
               'hazard area'),
            tr('Total population: %s') % format_int(total)
        ])
        impact_summary = Table(table_body).toNewlineFreeString()
        map_title = tr('People in high hazard areas')

        # Generate 8 equidistant classes across the range of flooded population
        # 8 is the number of classes in the predefined flood population style
        # as imported
        # noinspection PyTypeChecker
        classes = numpy.linspace(numpy.nanmin(M.flat[:]),
                                 numpy.nanmax(M.flat[:]), 8)

        # Modify labels in existing flood style to show quantities
        style_classes = style_info['style_classes']

        style_classes[1]['label'] = tr('Low [%i people/cell]') % classes[1]
        style_classes[4]['label'] = tr('Medium [%i people/cell]') % classes[4]
        style_classes[7]['label'] = tr('High [%i people/cell]') % classes[7]

        style_info['legend_title'] = tr('Population Density')

        # Create raster object and return
        R = Raster(M,
                   projection=my_hazard.get_projection(),
                   geotransform=my_hazard.get_geotransform(),
                   name=tr('Population which %s') %
                   (get_function_title(self).lower()),
                   keywords={
                       'impact_summary': impact_summary,
                       'impact_table': impact_table,
                       'map_title': map_title
                   },
                   style_info=style_info)
        return R
Exemplo n.º 40
0
def convert_netcdf2tif(filename, n, verbose=False, output_dir=None):

    """Convert netcdf to tif aggregating first n bands

    Args
        * filename: NetCDF multiband raster with extension .nc
        * n: Positive integer determining how many bands to use
        * verbose: Boolean flag controlling whether diagnostics
          will be printed to screen. This is useful when run from
          a command line script.

    Returns
        * Raster file in tif format. Each pixel will be the maximum
          of that pixel in the first n bands in the input file.

    """

    if not isinstance(filename, basestring):
        msg = "Argument filename should be a string. I got %s" % filename
        raise RuntimeError(msg)

    basename, ext = os.path.splitext(filename)
    msg = "Expected NetCDF file with extension .nc - " "Instead I got %s" % filename
    if ext != ".nc":
        raise RuntimeError(msg)

    try:
        n = int(n)
    except:
        msg = "Argument N should be an integer. I got %s" % n
        raise RuntimeError(msg)

    if verbose:
        print filename, n, "hours"

    # Read NetCDF file
    fid = NetCDFFile(filename)
    dimensions = fid.dimensions.keys()
    variables = fid.variables.keys()

    title = getattr(fid, "title")
    institution = getattr(fid, "institution")
    source = getattr(fid, "source")
    history = getattr(fid, "history")
    references = getattr(fid, "references")
    conventions = getattr(fid, "Conventions")
    coordinate_system = getattr(fid, "coordinate_system")

    if verbose:
        print "Read from %s" % filename
        print "Title: %s" % title
        print "Institution: %s" % institution
        print "Source: %s" % source
        print "History: %s" % history
        print "References: %s" % references
        print "Conventions: %s" % conventions
        print "Coordinate system: %s" % coordinate_system

        print "Dimensions: %s" % dimensions
        print "Variables:  %s" % variables

    # Get data
    x = fid.variables["x"][:]
    y = fid.variables["y"][:]
    # t = fid.variables['time'][:]
    inundation_depth = fid.variables["Inundation_Depth"][:]

    T = inundation_depth.shape[0]  # Number of time steps
    M = inundation_depth.shape[1]  # Steps in the y direction
    N = inundation_depth.shape[2]  # Steps in the x direction

    if n > T:
        msg = "You requested %i hours prediction, but the " "forecast only contains %i hours" % (n, T)
        raise RuntimeError(msg)

    # Compute the max of the first n timesteps
    A = numpy.zeros((M, N), dtype="float")
    for i in range(n):
        B = inundation_depth[i, :, :]
        A = numpy.maximum(A, B)

        # Calculate overall maximal value
        total_max = numpy.max(A[:])
        # print i, numpy.max(B[:]), total_max

    geotransform = raster_geometry2geotransform(x, y)

    # Write result to tif file
    # NOTE: This assumes a default projection (WGS 84, geographic)
    date = os.path.split(basename)[-1].split("_")[0]

    if verbose:
        print "Overall max depth over %i hours: %.2f m" % (n, total_max)
        print "Geotransform", geotransform
        print "date", date

    # Flip array upside down as it comes with rows ordered from south to north
    A = numpy.flipud(A)

    R = Raster(
        data=A,
        geotransform=geotransform,
        keywords={
            "category": "hazard",
            "subcategory": "flood",
            "unit": "m",
            "title": ("%d hour flood forecast grid " "in Jakarta at %s" % (n, date)),
        },
    )

    tif_filename = "%s_%d_hours_max_%.2f.tif" % (basename, n, total_max)
    if output_dir is not None:
        subdir_name = os.path.splitext(os.path.basename(tif_filename))[0]
        shapefile_dir = os.path.join(output_dir, subdir_name)
        if not os.path.isdir(shapefile_dir):
            os.mkdir(shapefile_dir)
        tif_filename = os.path.join(shapefile_dir, subdir_name + ".tif")

    R.write_to_file(tif_filename)

    if verbose:
        print "Success: %d hour forecast written to %s" % (n, R.filename)

    return tif_filename
Exemplo n.º 41
0
    def run(self):
        """Plugin for impact of population as derived by continuous hazard.

        Hazard is reclassified into 3 classes based on the extrema provided
        as impact function parameters.

        Counts number of people exposed to each category of the hazard

        :returns:
          Map of population exposed to high category
          Table with number of people in each category
        """

        thresholds = [
            p.value for p in self.parameters['Categorical thresholds'].value
        ]

        # Thresholds must contain 3 thresholds
        if len(thresholds) != 3:
            raise FunctionParametersError(
                'The thresholds must consist of 3 values.')

        # Thresholds must monotonically increasing
        monotonically_increasing_flag = all(
            x < y for x, y in zip(thresholds, thresholds[1:]))
        if not monotonically_increasing_flag:
            raise FunctionParametersError(
                'Each threshold should be larger than the previous.')

        # The 3 categories
        low_t = thresholds[0]
        medium_t = thresholds[1]
        high_t = thresholds[2]

        # Extract data as numeric arrays
        hazard_data = self.hazard.layer.get_data(nan=True)  # Category
        if has_no_data(hazard_data):
            self.no_data_warning = True

        # Calculate impact as population exposed to each category
        exposure_data = self.exposure.layer.get_data(nan=True, scaling=True)
        if has_no_data(exposure_data):
            self.no_data_warning = True

        # Make 3 data for each zone. Get the value of the exposure if the
        # exposure is in the hazard zone, else just assign 0
        low_exposure = numpy.where(hazard_data < low_t, exposure_data, 0)
        medium_exposure = numpy.where(
            (hazard_data >= low_t) & (hazard_data < medium_t), exposure_data,
            0)
        high_exposure = numpy.where(
            (hazard_data >= medium_t) & (hazard_data <= high_t), exposure_data,
            0)
        impacted_exposure = low_exposure + medium_exposure + high_exposure

        # Count totals
        self.total_population = int(numpy.nansum(exposure_data))
        self.affected_population[tr('Population in high hazard zones')] = int(
            numpy.nansum(high_exposure))
        self.affected_population[tr(
            'Population in medium hazard zones')] = int(
                numpy.nansum(medium_exposure))
        self.affected_population[tr('Population in low hazard zones')] = int(
            numpy.nansum(low_exposure))
        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)

        # Don't show digits less than a 1000
        self.minimum_needs = [
            parameter.serialize() for parameter in filter_needs_parameters(
                self.parameters['minimum needs'])
        ]
        total_needs = self.total_needs

        # Style for impact layer
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(impacted_exposure.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]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(target_field=None,
                          style_classes=style_classes,
                          style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            data=impacted_exposure,
            projection=self.hazard.layer.get_projection(),
            geotransform=self.hazard.layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 42
0
    def run(self):
        """Indonesian Earthquake Fatality Model."""
        displacement_rate = self.hardcoded_parameters['displacement_rate']
        fatality_rate = self.compute_fatality_rate()

        # Extract data grids
        hazard = self.hazard.layer.get_data()   # Ground Shaking
        # Population Density
        exposure = self.exposure.layer.get_data(scaling=True)

        # Calculate people affected by each MMI level
        mmi_range = self.hardcoded_parameters['mmi_range']
        number_of_exposed = {}
        number_of_displaced = {}
        number_of_fatalities = {}
        # Calculate fatality rates for observed Intensity values (hazard
        # based on ITB power model
        mask = numpy.zeros(hazard.shape)
        for mmi in mmi_range:
            # Identify cells where MMI is in class i and
            # count people affected by this shake level
            step = self.hardcoded_parameters['step']
            mmi_matches = numpy.where(
                (hazard > mmi - step) * (hazard <= mmi + step), exposure, 0)

            # Calculate expected number of fatalities per level
            exposed = numpy.nansum(mmi_matches)
            fatalities = fatality_rate[mmi] * exposed

            # Calculate expected number of displaced people per level
            displacements = displacement_rate[mmi] * (
                exposed - numpy.median(fatalities))

            # Adjust displaced people to disregard fatalities.
            # Set to zero if there are more fatalities than displaced.
            # displacements = numpy.where(
            #    displacements > fatalities, displacements - fatalities, 0)

            # Sum up numbers for map
            # We need to use matrices here and not just numbers #2235
            # filter out NaN to avoid overflow additions
            mmi_matches = numpy.nan_to_num(mmi_matches)
            mask += mmi_matches   # Displaced

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = exposed
            number_of_displaced[mmi] = displacements
            # noinspection PyUnresolvedReferences
            number_of_fatalities[mmi] = fatalities

        # Total statistics
        total_fatalities_raw = numpy.nansum(
            number_of_fatalities.values(), axis=0)

        # Compute probability of fatality in each magnitude bin
        if (self.__class__.__name__ == 'PAGFatalityFunction') or (
                self.__class__.__name__ == 'ITBBayesianFatalityFunction'):
            prob_fatality_mag = self.compute_probability(total_fatalities_raw)
        else:
            prob_fatality_mag = None

        # Compute number of fatalities
        self.total_population = numpy.nansum(number_of_exposed.values())
        self.total_fatalities = numpy.median(total_fatalities_raw)
        total_displaced = numpy.nansum(number_of_displaced.values())

        # As per email discussion with Ole, Trevor, Hadi, total fatalities < 50
        # will be rounded down to 0 - Tim
        # Needs to revisit but keep it alive for the time being - Hyeuk, Jono
        if self.total_fatalities < 50:
            self.total_fatalities = 0

        affected_population = self.affected_population
        affected_population[tr('Number of fatalities')] = self.total_fatalities
        affected_population[
            tr('Number of people displaced')] = total_displaced
        self.unaffected_population = (
            self.total_population - total_displaced - self.total_fatalities)
        self._evacuation_category = tr('Number of people displaced')

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]
        total_needs = self.total_needs

        # Create style
        colours = ['#EEFFEE', '#FFFF7F', '#E15500', '#E4001B', '#730000']
        classes = create_classes(mask.flat[:], len(colours))
        interval_classes = humanize_class(classes)
        style_classes = []
        for i in xrange(len(interval_classes)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            style_class['quantity'] = classes[i]
            style_class['transparency'] = 30
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(target_field=None,
                          style_classes=style_classes,
                          style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'exposed_per_mmi': number_of_exposed,
            'total_population': self.total_population,
            'total_fatalities': population_rounding(self.total_fatalities),
            'total_fatalities_raw': self.total_fatalities,
            'fatalities_per_mmi': number_of_fatalities,
            'total_displaced': population_rounding(total_displaced),
            'displaced_per_mmi': number_of_displaced,
            'map_title': self.metadata().key('map_title'),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs,
            'prob_fatality_mag': prob_fatality_mag,
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            mask,
            projection=self.exposure.layer.get_projection(),
            geotransform=self.exposure.layer.get_geotransform(),
            keywords=impact_layer_keywords,
            name=self.metadata().key('layer_name'),
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 43
0
    def run(self):
        """Risk plugin for flood population evacuation.

        Counts number of people exposed to flood levels exceeding
        specified threshold.

        :returns: Map of population exposed to flood levels exceeding the
            threshold. Table with number of people evacuated and supplies
            required.
        :rtype: tuple
        """
        self.validate()
        self.prepare()

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

        # Determine depths above which people are regarded affected [m]
        # Use thresholds from inundation layer if specified
        thresholds = self.parameters['thresholds'].value

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

        # Extract data as numeric arrays

        data = self.hazard.layer.get_data(nan=True)  # Depth
        if has_no_data(data):
            self.no_data_warning = True

        # Calculate impact as population exposed to depths > max threshold
        population = self.exposure.layer.get_data(nan=True, scaling=True)
        total = int(numpy.nansum(population))
        if has_no_data(population):
            self.no_data_warning = True

        # merely initialize
        impact = None

        for i, lo in enumerate(thresholds):
            if i == len(thresholds) - 1:
                # The last threshold
                thresholds_name = tr('People in >= %.1f m of water') % lo
                self.impact_category_ordering.append(thresholds_name)
                self._evacuation_category = thresholds_name
                impact = medium = numpy.where(data >= lo, population, 0)
            else:
                # Intermediate thresholds
                hi = thresholds[i + 1]
                thresholds_name = tr('People in %.1f m to %.1f m of water' %
                                     (lo, hi))
                self.impact_category_ordering.append(thresholds_name)
                medium = numpy.where((data >= lo) * (data < hi), population, 0)

            # Count
            val = int(numpy.nansum(medium))
            self.affected_population[thresholds_name] = val

        # Put the deepest area in top #2385
        self.impact_category_ordering.reverse()

        self.total_population = total
        self.unaffected_population = total - self.total_affected_population

        # Carry the no data values forward to the impact layer.
        impact = numpy.where(numpy.isnan(population), numpy.nan, impact)
        impact = numpy.where(numpy.isnan(data), numpy.nan, impact)

        # Count totals
        evacuated = self.total_evacuated

        self.minimum_needs = [
            parameter.serialize()
            for parameter in self.parameters['minimum needs']
        ]

        # Result
        impact_summary = self.html_report()
        impact_table = impact_summary

        total_needs = self.total_needs

        # check for zero impact
        if numpy.nanmax(impact) == 0 == numpy.nanmin(impact):
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        # Create style
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600',
            '#FF0000', '#7A0000'
        ]
        classes = create_classes(impact.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], 'Low')
            elif i == 4:
                label = create_label(interval_classes[i], 'Medium')
            elif i == 7:
                label = create_label(interval_classes[i], 'High')
            else:
                label = create_label(interval_classes[i])
            style_class['label'] = label
            style_class['quantity'] = classes[i]
            style_class['transparency'] = 0
            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

        # For printing map purpose
        map_title = tr('People in need of evacuation')
        legend_title = tr('Population Count')
        legend_units = tr('(people per cell)')
        legend_notes = tr('Thousand separator is represented by %s' %
                          get_thousand_separator())

        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,
            'evacuated': evacuated,
            'total_needs': total_needs
        }

        self.set_if_provenance()

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        raster = Raster(
            impact,
            projection=self.hazard.layer.get_projection(),
            geotransform=self.hazard.layer.get_geotransform(),
            name=tr('Population which %s') %
            (self.impact_function_manager.get_function_title(self).lower()),
            keywords=impact_layer_keywords,
            style_info=style_info)
        self._impact = raster
        return raster
Exemplo n.º 44
0
    def run(self, layers):
        """Plugin for impact of population as derived by categorised hazard

        Input
          layers: List of layers expected to contain
              H: Raster layer of categorised hazard
              P: Raster layer of population data

        Counts number of people exposed to each category of the hazard

        Return
          Map of population exposed to high category
          Table with number of people in each category
        """

        # The 3 category
        high_t = 1
        medium_t = 0.66
        low_t = 0.34

        # Identify hazard and exposure layers
        inundation = get_hazard_layer(layers)    # Categorised Hazard
        population = get_exposure_layer(layers)  # Population Raster

        question = get_question(inundation.get_name(),
                                population.get_name(),
                                self)

        # Extract data as numeric arrays
        C = inundation.get_data(nan=0.0)  # Category

        # Calculate impact as population exposed to each category
        P = population.get_data(nan=0.0, scaling=True)
        H = numpy.where(C == high_t, P, 0)
        M = numpy.where(C > medium_t, P, 0)
        L = numpy.where(C < low_t, P, 0)

        # Count totals
        total = int(numpy.sum(P))
        high = int(numpy.sum(H))
        medium = int(numpy.sum(M)) - int(numpy.sum(H))
        low = int(numpy.sum(L)) - int(numpy.sum(M))
        total_impact = high + medium + low

        # Don't show digits less than a 1000
        if total > 1000:
            total = total // 1000 * 1000
        if total_impact > 1000:
            total_impact = total_impact // 1000 * 1000
        if high > 1000:
            high = high // 1000 * 1000
        if medium > 1000:
            medium = medium // 1000 * 1000
        if low > 1000:
            low = low // 1000 * 1000

        # Calculate estimated needs based on BNPB Perka 7/2008 minimum bantuan
##        rice = evacuated * 2.8
##        drinking_water = evacuated * 17.5
##        water = evacuated * 67
##        family_kits = evacuated / 5
##        toilets = evacuated / 20

        # Generate impact report for the pdf map
        table_body = [question,
                      TableRow([tr('People impacted '),
                                '%i' % total_impact],
                               header=True),
                      TableRow([tr('People in high hazard area '),
                                '%i' % high],
                               header=True),
                      TableRow([tr('People in medium hazard area '),
                                '%i' % medium],
                               header=True),
                      TableRow([tr('People in low hazard area'),
                                '%i' % low],
                               header=True)]

##                    TableRow([tr('Needs per week'), tr('Total')],
##                               header=True),
##                      [tr('Rice [kg]'), int(rice)],
##                      [tr('Drinking Water [l]'), int(drinking_water)],
##                      [tr('Clean Water [l]'), int(water)],
##                      [tr('Family Kits'), int(family_kits)],
##                      [tr('Toilets'), int(toilets)]]
        impact_table = Table(table_body).toNewlineFreeString()

        # Extend impact report for on-screen display
        table_body.extend([TableRow(tr('Notes'), header=True),
                           tr('Map shows population density in high or medium '
                             'hazard area'),
                           tr('Total population: %i') % total])
##                           tr('Minimum needs are defined in BNPB '
##                             'regulation 7/2008')])
        impact_summary = Table(table_body).toNewlineFreeString()
        map_title = tr('People in high hazard areas')

        # Generare 8 equidistant classes across the range of flooded population
        # 8 is the number of classes in the predefined flood population style
        # as imported
        classes = numpy.linspace(numpy.nanmin(M.flat[:]),
                                 numpy.nanmax(M.flat[:]), 8)

        # Modify labels in existing flood style to show quantities
        style_classes = style_info['style_classes']

        style_classes[1]['label'] = tr('Low [%i people/cell]') % classes[1]
        style_classes[4]['label'] = tr('Medium [%i people/cell]') % classes[4]
        style_classes[7]['label'] = tr('High [%i people/cell]') % classes[7]

        style_info['legend_title'] = tr('Population Density')

        # Create raster object and return
        R = Raster(M,
                   projection=inundation.get_projection(),
                   geotransform=inundation.get_geotransform(),
                   name=tr('Population which %s') % get_function_title(self),
                   keywords={'impact_summary': impact_summary,
                             'impact_table': impact_table,
                             'map_title': map_title},
                   style_info=style_info)
        return R
Exemplo n.º 45
0
    def run(self):
        """Run volcano point population evacuation Impact Function.

        Counts number of people exposed to volcano event.

        :returns: Map of population exposed to the volcano hazard zone.
            The returned dict will include a table with number of people
            evacuated and supplies required.
        :rtype: dict

        :raises:
            * Exception - When hazard layer is not vector layer
            * RadiiException - When radii are not valid (they need to be
                monotonically increasing)
        """

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

        # Get parameters from layer's keywords
        volcano_name_attribute = self.hazard.keyword('volcano_name_field')

        data_table = self.hazard.layer.get_data()

        # Get names of volcanoes considered
        if volcano_name_attribute in self.hazard.layer.get_attribute_names():
            volcano_name_list = []
            # Run through all polygons and get unique names
            for row in data_table:
                volcano_name_list.append(row[volcano_name_attribute])

            volcano_names = ''
            for radius in volcano_name_list:
                volcano_names += '%s, ' % radius
            self.volcano_names = volcano_names[:-2]  # Strip trailing ', '

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

        # Initialise affected population per categories
        for radius in radii:
            category = 'Radius %s km ' % format_int(radius)
            self.affected_population[category] = 0

        if has_no_data(self.exposure.layer.get_data(nan=True)):
            self.no_data_warning = True
        # Count affected population per polygon and total
        for row in interpolated_layer.get_data():
            # Get population at this location
            population = row[self.target_field]
            if not numpy.isnan(population):
                population = float(population)
                # Update population count for this category
                category = 'Radius %s km ' % format_int(
                    row[self.hazard_zone_attribute])
                self.affected_population[category] += population

        # Count totals
        self.total_population = population_rounding(
            int(numpy.nansum(self.exposure.layer.get_data())))

        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
        ]

        # Create style
        colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00',
                   '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(
            covered_exposure_layer.get_data().flat[:], len(colours))
        interval_classes = humanize_class(classes)
        # Define style info for output polygons showing population counts
        style_classes = []
        for i in xrange(len(colours)):
            style_class = dict()
            style_class['label'] = create_label(interval_classes[i])
            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]
            style_class['colour'] = colours[i]
            style_class['transparency'] = 0
            style_classes.append(style_class)

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

        impact_data = self.generate_data()

        # Create vector layer and return
        extra_keywords = {
            'target_field': self.target_field,
            'map_title': self.metadata().key('map_title'),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': self.total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        impact_layer = Raster(
            data=covered_exposure_layer.get_data(),
            projection=covered_exposure_layer.get_projection(),
            geotransform=covered_exposure_layer.get_geotransform(),
            name=self.metadata().key('layer_name'),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 46
0
    def run(self):
        """Indonesian Earthquake Fatality Model.

        Some additional notes to clarify behaviour:

        * Total population = all people within the analysis area
        * Affected population  = displaced people + people killed
        * Displaced = people * displacement rate for mmi level
        * Killed = people * mortality rate for mmi level
        * impact layer produced = affected population

        """
        displacement_rate = self.hardcoded_parameters["displacement_rate"]
        fatality_rate = self.compute_fatality_rate()

        # Extract data grids
        hazard = self.hazard.layer.get_data()  # Ground Shaking
        # Population Density
        exposure = self.exposure.layer.get_data(scaling=True)

        # Calculate people affected by each MMI level
        mmi_range = self.hardcoded_parameters["mmi_range"]
        number_of_exposed = {}
        number_of_displaced = {}
        number_of_fatalities = {}
        # Calculate fatality rates for observed Intensity values (hazard
        # based on ITB power model
        mask = numpy.zeros(hazard.shape)
        for mmi in mmi_range:
            # Identify cells where MMI is in class i and
            # count people affected by this shake level
            step = self.hardcoded_parameters["step"]
            mmi_matches = numpy.where((hazard > mmi - step) * (hazard <= mmi + step), exposure, 0)

            # Calculate expected number of fatalities per level
            exposed = numpy.nansum(mmi_matches)
            fatalities = fatality_rate[mmi] * exposed

            # Calculate expected number of displaced people per level
            displacements = displacement_rate[mmi] * (exposed - numpy.median(fatalities))

            # Adjust displaced people to disregard fatalities.
            # Set to zero if there are more fatalities than displaced.
            # displacements = numpy.where(
            #    displacements > fatalities, displacements - fatalities, 0)

            # Sum up numbers for map
            # We need to use matrices here and not just numbers #2235
            # filter out NaN to avoid overflow additions
            # Changed in 3.5.3 for Issue #3489 to correct mask
            # to that it returns affected (displaced + fatalities)
            mmi_matches = displacement_rate[mmi] * numpy.nan_to_num(mmi_matches)
            mask += mmi_matches  # Displaced

            # Generate text with result for this study
            # This is what is used in the real time system exposure table
            number_of_exposed[mmi] = exposed
            number_of_displaced[mmi] = displacements
            # noinspection PyUnresolvedReferences
            number_of_fatalities[mmi] = fatalities

        # Total statistics
        total_fatalities_raw = numpy.nansum(number_of_fatalities.values(), axis=0)

        # Compute probability of fatality in each magnitude bin
        if (self.__class__.__name__ == "PAGFatalityFunction") or (
            self.__class__.__name__ == "ITBBayesianFatalityFunction"
        ):
            prob_fatality_mag = self.compute_probability(total_fatalities_raw)
        else:
            prob_fatality_mag = None

        # Compute number of fatalities
        self.total_population = numpy.nansum(number_of_exposed.values())
        self.total_fatalities = numpy.median(total_fatalities_raw)
        total_displaced = numpy.nansum(number_of_displaced.values())

        # As per email discussion with Ole, Trevor, Hadi, total fatalities < 50
        # will be rounded down to 0 - Tim
        # Needs to revisit but keep it alive for the time being - Hyeuk, Jono
        if self.total_fatalities < 50:
            self.total_fatalities = 0

        affected_population = self.affected_population
        affected_population[tr("Number of fatalities")] = self.total_fatalities
        affected_population[tr("Number of people displaced")] = total_displaced
        self.unaffected_population = self.total_population - total_displaced - self.total_fatalities
        self._evacuation_category = tr("Number of people displaced")

        self.minimum_needs = [
            parameter.serialize() for parameter in filter_needs_parameters(self.parameters["minimum needs"])
        ]
        total_needs = self.total_needs

        # Create style
        colours = ["#EEFFEE", "#FFFF7F", "#E15500", "#E4001B", "#730000"]
        classes = create_classes(mask.flat[:], len(colours))
        interval_classes = humanize_class(classes)
        style_classes = []
        for i in xrange(len(interval_classes)):
            style_class = dict()
            style_class["label"] = create_label(interval_classes[i])
            style_class["quantity"] = classes[i]
            style_class["transparency"] = 30
            style_class["colour"] = colours[i]
            style_classes.append(style_class)

        style_info = dict(target_field=None, style_classes=style_classes, style_type="rasterStyle")

        impact_data = self.generate_data()

        extra_keywords = {
            "exposed_per_mmi": number_of_exposed,
            "total_population": self.total_population,
            "total_fatalities": population_rounding(self.total_fatalities),
            "total_fatalities_raw": self.total_fatalities,
            "fatalities_per_mmi": number_of_fatalities,
            "total_displaced": population_rounding(total_displaced),
            "displaced_per_mmi": number_of_displaced,
            "map_title": self.map_title(),
            "legend_notes": self.metadata().key("legend_notes"),
            "legend_units": self.metadata().key("legend_units"),
            "legend_title": self.metadata().key("legend_title"),
            "total_needs": total_needs,
            "prob_fatality_mag": prob_fatality_mag,
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            mask,
            projection=self.exposure.layer.get_projection(),
            geotransform=self.exposure.layer.get_geotransform(),
            keywords=impact_layer_keywords,
            name=self.map_title(),
            style_info=style_info,
        )

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer
Exemplo n.º 47
0
def convert_netcdf2tif(filename, n):
    """Convert netcdf to tif aggregating firsts n bands
    """

    if not isinstance(filename, basestring):
        msg = 'Argument filename should be a string. I got %s' % filename
        raise RuntimeError(msg)

    basename, ext = os.path.splitext(filename)
    msg = ('Expected NetCDF file with extension .nc - '
           'Instead I got %s' % filename)
    if ext != '.nc':
        raise RuntimeError(msg)

    try:
        n = int(n)
    except:
        msg = 'Argument N should be an integer. I got %s' % n
        raise RuntimeError(msg)

    print filename, n

    # Read NetCDF file
    fid = NetCDFFile(filename)
    dimensions = fid.dimensions.keys()
    variables = fid.variables.keys()

    title = getattr(fid, 'title')
    institution = getattr(fid, 'institution')
    source = getattr(fid, 'source')
    history = getattr(fid, 'history')
    references = getattr(fid, 'references')
    conventions = getattr(fid, 'Conventions')
    coordinate_system = getattr(fid, 'coordinate_system')

    print 'Read from %s' % filename
    print 'Title: %s' % title
    print 'Institution: %s' % institution
    print 'Source: %s' % source
    print 'History: %s' % history
    print 'References: %s' % references
    print 'Conventions: %s' % conventions
    print 'Coordinate system: %s' % coordinate_system

    print 'Dimensions: %s' % dimensions
    print 'Variables:  %s' % variables

    # Get data
    x = fid.variables['x'][:]
    y = fid.variables['y'][:]
    t = fid.variables['time'][:]
    inundation_depth = fid.variables['Inundation_Depth'][:]

    T = inundation_depth.shape[0]  # Number of time steps
    M = inundation_depth.shape[1]  # Steps in the y direction
    N = inundation_depth.shape[2]  # Steps in the x direction

    # Compute the max of the first n timesteps
    A = numpy.zeros((M, N), dtype='float')
    for i in range(n):
        B = inundation_depth[i, :, :]
        A = numpy.maximum(A, B)

    geotransform = raster_geometry2geotransform(x, y)
    print 'Geotransform', geotransform

    # Write result to tif file
    R = Raster(data=A,
               projection="""PROJCS["DGN95 / Indonesia TM-3 zone 48.2",
                             GEOGCS["DGN95",
                                 DATUM["Datum_Geodesi_Nasional_1995",
                                     SPHEROID["WGS 84",6378137,298.257223563,
                                         AUTHORITY["EPSG","7030"]],
                                     TOWGS84[0,0,0,0,0,0,0],
                                     AUTHORITY["EPSG","6755"]],
                                 PRIMEM["Greenwich",0,
                                     AUTHORITY["EPSG","8901"]],
                                 UNIT["degree",0.01745329251994328,
                                     AUTHORITY["EPSG","9122"]],
                                 AUTHORITY["EPSG","4755"]],
                             UNIT["metre",1,
                                 AUTHORITY["EPSG","9001"]],
                             PROJECTION["Transverse_Mercator"],
                             PARAMETER["latitude_of_origin",0],
                             PARAMETER["central_meridian",106.5],
                             PARAMETER["scale_factor",0.9999],
                             PARAMETER["false_easting",200000],
                             PARAMETER["false_northing",1500000],
                             AUTHORITY["EPSG","23834"],
                             AXIS["X",EAST],
                             AXIS["Y",NORTH]]""",
               geotransform=geotransform,
               keywords={'category': 'hazard',
                         'subcategory': 'flood',
                         'unit': 'm',
                         'title': ('Hypothetical %d hour flood forecast '
                                   'in Jakarta' % n)})
    R.write_to_file('%s_%d_hours.tif' % (basename, n))
    print 'Success: %d hour forecast written to %s' % (n, R.filename)
Exemplo n.º 48
0
    def run(self):
        """Run the impact function.
        """
        # Range for ash hazard
        group_parameters = self.parameters['group_threshold']
        unaffected_max = group_parameters.value_map[
            'unaffected_threshold'].value
        very_low_max = group_parameters.value_map['very_low_threshold'].value
        low_max = group_parameters.value_map['low_threshold'].value
        medium_max = group_parameters.value_map['moderate_threshold'].value
        high_max = group_parameters.value_map['high_threshold'].value

        # Extract hazard data as numeric arrays
        ash = self.hazard.layer.get_data(nan=True)  # Thickness
        if has_no_data(ash):
            self.no_data_warning = True

        # Extract exposure data as numeric arrays
        population = self.exposure.layer.get_data(nan=True, scaling=True)
        if has_no_data(population):
            self.no_data_warning = True

        # Create 5 data for each hazard level. Get the value of the exposure
        # if the exposure is in the hazard zone, else just assign 0
        unaffected_exposure = numpy.where(ash < unaffected_max, population, 0)
        very_low_exposure = numpy.where(
            (ash >= unaffected_max) & (ash < very_low_max), population, 0)
        low_exposure = numpy.where(
            (ash >= very_low_max) & (ash < low_max), population, 0)
        medium_exposure = numpy.where(
            (ash >= low_max) & (ash < medium_max), population, 0)
        high_exposure = numpy.where(
            (ash >= medium_max) & (ash < high_max), population, 0)
        very_high_exposure = numpy.where(ash >= high_max, population, 0)

        impacted_exposure = (
            very_low_exposure +
            low_exposure +
            medium_exposure +
            high_exposure +
            very_high_exposure
        )

        # Count totals
        self.total_population = int(numpy.nansum(population))
        self.affected_population[
            tr('Population in very low hazard zone')] = int(
            numpy.nansum(very_low_exposure))
        self.affected_population[
            tr('Population in low hazard zone')] = int(
            numpy.nansum(low_exposure))
        self.affected_population[
            tr('Population in medium hazard zone')] = int(
            numpy.nansum(medium_exposure))
        self.affected_population[
            tr('Population in high hazard zone')] = int(
            numpy.nansum(high_exposure))
        self.affected_population[
            tr('Population in very high hazard zone')] = int(
            numpy.nansum(very_high_exposure))
        self.unaffected_population = int(
            numpy.nansum(unaffected_exposure))

        # check for zero impact
        if self.total_affected_population == 0:
            message = no_population_impact_message(self.question)
            raise ZeroImpactException(message)

        # Don't show digits less than a 1000
        self.minimum_needs = [
            parameter.serialize() for parameter in
            filter_needs_parameters(self.parameters['minimum needs'])
            ]
        total_needs = self.total_needs

        # Style for impact layer
        colours = [
            '#FFFFFF', '#38A800', '#79C900', '#CEED00',
            '#FFCC00', '#FF6600', '#FF0000', '#7A0000']
        classes = create_classes(impacted_exposure.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]
            style_class['transparency'] = 0
            style_class['colour'] = colours[i]
            style_classes.append(style_class)

        style_info = dict(
            target_field=None,
            style_classes=style_classes,
            style_type='rasterStyle')

        impact_data = self.generate_data()

        extra_keywords = {
            'map_title': self.map_title(),
            'legend_notes': self.metadata().key('legend_notes'),
            'legend_units': self.metadata().key('legend_units'),
            'legend_title': self.metadata().key('legend_title'),
            'total_needs': total_needs
        }

        impact_layer_keywords = self.generate_impact_keywords(extra_keywords)

        # Create raster object and return
        impact_layer = Raster(
            data=impacted_exposure,
            projection=self.hazard.layer.get_projection(),
            geotransform=self.hazard.layer.get_geotransform(),
            name=self.map_title(),
            keywords=impact_layer_keywords,
            style_info=style_info)

        impact_layer.impact_data = impact_data
        self._impact = impact_layer
        return impact_layer