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=None): """Risk plugin for tsunami population evacuation. :param layers: List of layers expected to contain hazard_layer: Raster layer of tsunami depth exposure_layer: Raster layer of population data on the same grid as hazard_layer 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 """ self.validate() self.prepare(layers) # Identify hazard and exposure layers hazard_layer = self.hazard # Tsunami inundation [m] exposure_layer = self.exposure # 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 data = hazard_layer.get_data(nan=True) # Depth no_data_warning = False if has_no_data(data): no_data_warning = True # Calculate impact as population exposed to depths > max threshold population = exposure_layer.get_data(nan=True, scaling=True) if has_no_data(population): no_data_warning = True # Calculate impact to intermediate thresholds counts = [] # merely initialize impact = None for i, lo in enumerate(thresholds): if i == len(thresholds) - 1: # The last threshold impact = medium = numpy.where(data >= lo, population, 0) else: # Intermediate thresholds hi = thresholds[i + 1] medium = numpy.where((data >= lo) * (data < hi), population, 0) # Count val = int(numpy.nansum(medium)) # Sensible rounding val, rounding = population_rounding_full(val) counts.append([val, rounding]) # 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, rounding = counts[-1] total = int(numpy.nansum(population)) # Don't show digits less than a 1000 total = population_rounding(total) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Generate impact report for the pdf map table_body, total_needs = self._tabulate( counts, evacuated, minimum_needs, self.question, rounding, thresholds, total, no_data_warning) # Result impact_summary = Table(table_body).toNewlineFreeString() impact_table = impact_summary # check for zero impact if numpy.nanmax(impact) == 0 == numpy.nanmin(impact): table_body = [ self.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(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') # Create raster object and return raster = Raster( impact, projection=hazard_layer.get_projection(), geotransform=hazard_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, 'evacuated': evacuated, 'total_needs': total_needs}, style_info=style_info) self._impact = raster return raster
def test_0005_has_no_data(self): """Test whether the nodata tester detects nan's in arrays.""" layer_data = numpy.array([[1, 2.0], [2.0, 3]]) layer_data_nan = numpy.array([[1, 2.0], [numpy.nan, 3]]) self.assertFalse(has_no_data(layer_data)) self.assertTrue(has_no_data(layer_data_nan))
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
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
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
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 """ # 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'] ] 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') 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': evacuated, 'total_needs': 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
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
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.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.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
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(): # Run through all polygons and get unique names for row in data_table: self.volcano_names.add(row[volcano_name_attribute]) # 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 impact_category_ordering = [] for radius in radii: category = tr('Radius %s km ' % format_int(radius)) self.affected_population[category] = 0 impact_category_ordering.append(category) self.impact_category_ordering = impact_category_ordering 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 = tr('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.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.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def run(self, layers=None): """Run volcano point population evacuation Impact Function. :param layers: List of layers expected to contain where two layers should be present. * hazard_layer: Vector point layer. * exposure_layer: Raster layer of population data on the same grid as hazard_layer 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(layers) # Parameters radii = self.parameters['distance [km]'] name_attribute = self.parameters['volcano name attribute'] # Identify hazard and exposure layers hazard_layer = self.hazard exposure_layer = self.exposure # Input checks if not hazard_layer.is_point_data: msg = ( 'Input hazard must be a polygon or point layer. I got %s with ' 'layer type %s' % (hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(msg) data_table = hazard_layer.get_data() # Use concentric circles category_title = 'Radius' category_header = tr('Distance [km]') centers = hazard_layer.get_geometry() rad_m = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points( centers, rad_m, category_title, data_table=data_table) # Get names of volcanoes considered if 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[name_attribute]) volcano_names = '' for radius in volcano_name_list: volcano_names += '%s, ' % radius volcano_names = volcano_names[:-2] # Strip trailing ', ' else: volcano_names = tr('Not specified in data') # Find the target field name that has no conflict with default target attribute_names = hazard_layer.get_attribute_names() new_target_field = get_non_conflicting_attribute_name( self.target_field, attribute_names) self.target_field = new_target_field # Run interpolation function for polygon2raster interpolated_layer, covered_exposure_layer = \ assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=self.target_field ) # Initialise affected population per categories affected_population = {} for radius in rad_m: affected_population[radius] = 0 nan_warning = False if has_no_data(exposure_layer.get_data(nan=True)): nan_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 = row[category_title] affected_population[category] += population # Count totals total_population = population_rounding( int(numpy.nansum(exposure_layer.get_data()))) # Count cumulative for each zone total_affected_population = 0 cumulative_affected_population = {} for radius in rad_m: population = int(affected_population.get(radius, 0)) total_affected_population += population cumulative_affected_population[radius] = total_affected_population minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Generate impact report for the pdf map blank_cell = '' table_body = [ self.question, TableRow( [tr('Volcanoes considered'), '%s' % volcano_names, blank_cell], header=True), TableRow( [tr('People needing evacuation'), '%s' % format_int( population_rounding(total_affected_population)), blank_cell], header=True), TableRow( [category_header, tr('Total'), tr('Cumulative')], header=True)] for radius in rad_m: table_body.append( TableRow( [radius, format_int( population_rounding( affected_population[radius])), format_int( population_rounding( cumulative_affected_population[radius]))])) table_body.extend([ TableRow(tr( 'Map shows the number of people affected in each of volcano ' 'hazard polygons.'))]) total_needs = evacuated_population_needs( total_affected_population, 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() # Extend impact report for on-screen display table_body.extend( [TableRow(tr('Notes'), header=True), tr('Total population %s in the exposure layer') % format_int( total_population), tr('People need evacuation if they are within the ' 'volcanic hazard zones.')]) if nan_warning: table_body.extend([ tr('The population layer contained `no data`. This missing ' 'data was carried through to the impact layer.'), tr('`No data` values in the impact layer were treated as 0 ' 'when counting the affected or total population.') ]) impact_summary = Table(table_body).toNewlineFreeString() # check for zero impact if total_affected_population == 0: table_body = [ self.question, TableRow( [tr('People needing evacuation'), '%s' % format_int(total_affected_population), blank_cell], header=True)] message = Table(table_body).toNewlineFreeString() 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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) legend_units = tr('(people per cell)') legend_title = tr('Population') # 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 the buffered point volcano'), 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': total_needs}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Risk plugin for tsunami population evacuation. :param layers: List of layers expected to contain hazard_layer: Raster layer of tsunami depth exposure_layer: Raster layer of population data on the same grid as hazard_layer 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 """ self.validate() self.prepare(layers) # Identify hazard and exposure layers hazard_layer = self.hazard # Tsunami inundation [m] exposure_layer = self.exposure # 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 data = hazard_layer.get_data(nan=True) # Depth no_data_warning = False if has_no_data(data): no_data_warning = True # Calculate impact as population exposed to depths > max threshold population = exposure_layer.get_data(nan=True, scaling=True) if has_no_data(population): no_data_warning = True # Calculate impact to intermediate thresholds counts = [] # merely initialize impact = None for i, lo in enumerate(thresholds): if i == len(thresholds) - 1: # The last threshold impact = medium = numpy.where(data >= lo, population, 0) else: # Intermediate thresholds hi = thresholds[i + 1] medium = numpy.where((data >= lo) * (data < hi), population, 0) # Count val = int(numpy.nansum(medium)) # Sensible rounding val, rounding = population_rounding_full(val) counts.append([val, rounding]) # 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, rounding = counts[-1] total = int(numpy.nansum(population)) # Don't show digits less than a 1000 total = population_rounding(total) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Generate impact report for the pdf map table_body, total_needs = self._tabulate(counts, evacuated, minimum_needs, self.question, rounding, thresholds, total, no_data_warning) # Result impact_summary = Table(table_body).toNewlineFreeString() impact_table = impact_summary # check for zero impact if numpy.nanmax(impact) == 0 == numpy.nanmin(impact): table_body = [ self.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(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') # Create raster object and return raster = Raster( impact, projection=hazard_layer.get_projection(), geotransform=hazard_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, 'evacuated': evacuated, 'total_needs': total_needs }, style_info=style_info) self._impact = raster return raster
def run(self, layers=None): """Run volcano point population evacuation Impact Function. :param layers: List of layers expected to contain where two layers should be present. * hazard_layer: Vector point layer. * exposure_layer: Raster layer of population data on the same grid as hazard_layer 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(layers) # Parameters radii = self.parameters['distance [km]'] name_attribute = self.parameters['volcano name attribute'] # Identify hazard and exposure layers hazard_layer = self.hazard exposure_layer = self.exposure # Input checks if not hazard_layer.is_point_data: msg = ( 'Input hazard must be a polygon or point layer. I got %s with ' 'layer type %s' % (hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(msg) data_table = hazard_layer.get_data() # Use concentric circles category_title = 'Radius' category_header = tr('Distance [km]') centers = hazard_layer.get_geometry() rad_m = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points(centers, rad_m, category_title, data_table=data_table) # Get names of volcanoes considered if 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[name_attribute]) volcano_names = '' for radius in volcano_name_list: volcano_names += '%s, ' % radius volcano_names = volcano_names[:-2] # Strip trailing ', ' else: volcano_names = tr('Not specified in data') # Find the target field name that has no conflict with default target attribute_names = hazard_layer.get_attribute_names() new_target_field = get_non_conflicting_attribute_name( self.target_field, attribute_names) self.target_field = new_target_field # Run interpolation function for polygon2raster interpolated_layer, covered_exposure_layer = \ assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=self.target_field ) # Initialise affected population per categories affected_population = {} for radius in rad_m: affected_population[radius] = 0 nan_warning = False if has_no_data(exposure_layer.get_data(nan=True)): nan_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 = row[category_title] affected_population[category] += population # Count totals total_population = population_rounding( int(numpy.nansum(exposure_layer.get_data()))) # Count cumulative for each zone total_affected_population = 0 cumulative_affected_population = {} for radius in rad_m: population = int(affected_population.get(radius, 0)) total_affected_population += population cumulative_affected_population[radius] = total_affected_population minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Generate impact report for the pdf map blank_cell = '' table_body = [ self.question, TableRow( [tr('Volcanoes considered'), '%s' % volcano_names, blank_cell], header=True), TableRow([ tr('People needing evacuation'), '%s' % format_int(population_rounding(total_affected_population)), blank_cell ], header=True), TableRow( [category_header, tr('Total'), tr('Cumulative')], header=True) ] for radius in rad_m: table_body.append( TableRow([ radius, format_int(population_rounding( affected_population[radius])), format_int( population_rounding( cumulative_affected_population[radius])) ])) table_body.extend([ TableRow( tr('Map shows the number of people affected in each of volcano ' 'hazard polygons.')) ]) total_needs = evacuated_population_needs(total_affected_population, 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() # Extend impact report for on-screen display table_body.extend([ TableRow(tr('Notes'), header=True), tr('Total population %s in the exposure layer') % format_int(total_population), tr('People need evacuation if they are within the ' 'volcanic hazard zones.') ]) if nan_warning: table_body.extend([ tr('The population layer contained `no data`. This missing ' 'data was carried through to the impact layer.'), tr('`No data` values in the impact layer were treated as 0 ' 'when counting the affected or total population.') ]) impact_summary = Table(table_body).toNewlineFreeString() # check for zero impact if total_affected_population == 0: table_body = [ self.question, TableRow([ tr('People needing evacuation'), '%s' % format_int(total_affected_population), blank_cell ], header=True) ] message = Table(table_body).toNewlineFreeString() 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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) legend_units = tr('(people per cell)') legend_title = tr('Population') # 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 the buffered point volcano'), 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': total_needs }, style_info=style_info) self._impact = impact_layer return impact_layer
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
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() # Parameters self.hazard_class_attribute = self.hazard.keyword('field') name_attribute = self.hazard.keyword('volcano_name_field') 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: msg = ('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(msg) # Check if hazard_class_attribute exists in hazard_layer if (self.hazard_class_attribute not in self.hazard.layer.get_attribute_names()): msg = ('Hazard data %s did not contain expected attribute %s ' % ( self.hazard.layer.get_name(), self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(msg) features = self.hazard.layer.get_data() hazard_zone_categories = list( set(self.hazard.layer.get_data(self.hazard_class_attribute))) # 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)) # 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 total affected per category for hazard_zone in hazard_zone_categories: self.affected_population[hazard_zone] = 0 # 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 = row[self.hazard_class_attribute] self.affected_population[category] += 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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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()) # 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_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}, style_info=style_info) self._impact = impact_layer return impact_layer
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() # 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() rad_m = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points( centers, rad_m, 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 rad_m: category = 'Distance %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 = 'Distance %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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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 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_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}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Risk plugin for flood population evacuation. :param layers: List of layers expected to contain * hazard_layer : Vector polygon layer of flood depth * exposure_layer : Raster layer of population data on the same grid as hazard_layer 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(layers) # Get the IF parameters affected_field = self.parameters['affected_field'] affected_value = self.parameters['affected_value'] evacuation_percentage = self.parameters['evacuation_percentage'] # Identify hazard and exposure layers hazard_layer = self.hazard exposure_layer = self.exposure # Check that hazard is polygon type if not hazard_layer.is_polygon_data: message = ( 'Input hazard must be a polygon layer. I got %s with layer ' 'type %s' % (hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(message) nan_warning = False if has_no_data(exposure_layer.get_data(nan=True)): nan_warning = True # Check that affected field exists in hazard layer if affected_field in 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( hazard_layer, 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[affected_field] if row_affected_value is not None: if isinstance(row_affected_value, Number): type_func = type(row_affected_value) affected = row_affected_value == type_func( affected_value) else: affected =\ get_unicode(affected_value).lower() == \ get_unicode(row_affected_value).lower() else: # assume that every polygon is affected (see #816) affected = True if affected: # 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 evacuated = (total_affected_population * evacuation_percentage / 100.0) total_population = int( numpy.nansum(exposure_layer.get_data(scaling=False))) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Rounding total_affected_population, rounding = population_rounding_full( total_affected_population) total_population = population_rounding(total_population) evacuated, rounding_evacuated = population_rounding_full(evacuated) # Generate impact report for the pdf map table_body, total_needs = self._tabulate(total_affected_population, evacuated, minimum_needs, self.question, rounding, rounding_evacuated) impact_table = Table(table_body).toNewlineFreeString() self._tabulate_action_checklist(table_body, total_population, nan_warning) impact_summary = Table(table_body).toNewlineFreeString() # 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 min(classes) == 0 == max(classes): table_body = [ self.question, TableRow([ tr('People affected'), '%s' % format_int(total_affected_population) ], header=True) ] message = Table(table_body).toNewlineFreeString() 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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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_notes = tr('Thousand separator is represented by \'.\'') legend_units = tr('(people per polygon)') legend_title = tr('Population Count') # 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_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': total_population, 'total_needs': total_needs }, style_info=style_info) self._impact = impact_layer return impact_layer
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
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
def run(self, layers=None): """Risk plugin for flood population evacuation. :param layers: List of layers expected to contain * hazard_layer : Vector polygon layer of flood depth * exposure_layer : Raster layer of population data on the same grid as hazard_layer 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(layers) # Get the IF parameters affected_field = self.parameters['affected_field'] affected_value = self.parameters['affected_value'] evacuation_percentage = self.parameters['evacuation_percentage'] # Identify hazard and exposure layers hazard_layer = self.hazard exposure_layer = self.exposure # Check that hazard is polygon type if not hazard_layer.is_polygon_data: message = ( 'Input hazard must be a polygon layer. I got %s with layer ' 'type %s' % ( hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(message) nan_warning = False if has_no_data(exposure_layer.get_data(nan=True)): nan_warning = True # Check that affected field exists in hazard layer if affected_field in 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( hazard_layer, 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[affected_field] if row_affected_value is not None: if isinstance(row_affected_value, Number): type_func = type(row_affected_value) affected = row_affected_value == type_func( affected_value) else: affected =\ get_unicode(affected_value).lower() == \ get_unicode(row_affected_value).lower() else: # assume that every polygon is affected (see #816) affected = True if affected: # 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 evacuated = ( total_affected_population * evacuation_percentage / 100.0) total_population = int( numpy.nansum(exposure_layer.get_data(scaling=False))) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] # Rounding total_affected_population, rounding = population_rounding_full( total_affected_population) total_population = population_rounding(total_population) evacuated, rounding_evacuated = population_rounding_full(evacuated) # Generate impact report for the pdf map table_body, total_needs = self._tabulate( total_affected_population, evacuated, minimum_needs, self.question, rounding, rounding_evacuated) impact_table = Table(table_body).toNewlineFreeString() self._tabulate_action_checklist( table_body, total_population, nan_warning) impact_summary = Table(table_body).toNewlineFreeString() # 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 min(classes) == 0 == max(classes): table_body = [ self.question, TableRow( [tr('People affected'), '%s' % format_int(total_affected_population)], header=True)] message = Table(table_body).toNewlineFreeString() 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]) if i == 0: transparency = 100 else: transparency = 0 style_class['label'] = label style_class['quantity'] = classes[i] style_class['colour'] = colours[i] style_class['transparency'] = transparency 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_notes = tr('Thousand separator is represented by \'.\'') legend_units = tr('(people per polygon)') legend_title = tr('Population Count') # 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_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': total_population, 'total_needs': total_needs}, style_info=style_info) self._impact = impact_layer return impact_layer
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
def run(self, layers=None): """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 """ self.validate() self.prepare(layers) thresholds = self.parameters['Categorical thresholds'] # 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] # Identify hazard and exposure layers hazard_layer = self.hazard # Categorised Hazard exposure_layer = self.exposure # Population Raster # Extract data as numeric arrays hazard_data = hazard_layer.get_data(nan=True) # Category no_data_warning = False if has_no_data(hazard_data): no_data_warning = True # Calculate impact as population exposed to each category exposure_data = exposure_layer.get_data(nan=True, scaling=True) if has_no_data(exposure_data): 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 total = int(numpy.nansum(exposure_data)) low_total = int(numpy.nansum(low_exposure)) medium_total = int(numpy.nansum(medium_exposure)) high_total = int(numpy.nansum(high_exposure)) total_impact = high_total + medium_total + low_total # Check for zero impact if total_impact == 0: table_body = [ self.question, TableRow( [tr('People impacted'), '%s' % format_int(total_impact)], header=True)] message = Table(table_body).toNewlineFreeString() raise ZeroImpactException(message) # Don't show digits less than a 1000 total = population_rounding(total) total_impact = population_rounding(total_impact) low_total = population_rounding(low_total) medium_total = population_rounding(medium_total) high_total = population_rounding(high_total) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] table_body = self._tabulate( high_total, low_total, medium_total, self.question, total_impact) impact_table = Table(table_body).toNewlineFreeString() table_body, total_needs = self._tabulate_notes( minimum_needs, table_body, total, total_impact, no_data_warning) impact_summary = Table(table_body).toNewlineFreeString() map_title = tr('People in each hazard areas (low, medium, high)') # 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] 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') # Create raster object and return raster_layer = Raster( data=impacted_exposure, projection=hazard_layer.get_projection(), geotransform=hazard_layer.get_geotransform(), name=tr('Population might %s') % ( self.impact_function_manager. 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) self._impact = raster_layer return raster_layer
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 """ self.validate() self.prepare() # 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 # 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']) ] impact_table = impact_summary = self.html_report() # 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] 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 # For printing map purpose map_title = tr('People in need of evacuation') legend_title = tr('Population') legend_units = tr('(people per cell)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) # 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_summary': impact_summary, 'impact_table': impact_table, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title, 'evacuated': self.total_evacuated, 'total_needs': self.total_needs}, style_info=style_info) self._impact = raster return raster
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
def run(self): """Plugin for impact of population as derived by classified hazard. Counts number of people exposed to each class of the hazard Return Map of population exposed to high class Table with number of people in each class """ self.validate() self.prepare() # The 3 classes # TODO (3.2): shouldnt these be defined in keywords rather? TS categorical_hazards = self.parameters['Categorical hazards'].value low_class = categorical_hazards[0].value medium_class = categorical_hazards[1].value high_class = categorical_hazards[2].value # The classes must be different to each other unique_classes_flag = all( x != y for x, y in list( itertools.combinations( [low_class, medium_class, high_class], 2))) if not unique_classes_flag: raise FunctionParametersError( 'There is hazard class that has the same value with other ' 'class. Please check the parameters.') # Extract data as numeric arrays hazard_data = self.hazard.layer.get_data(nan=True) # Class if has_no_data(hazard_data): self.no_data_warning = True # Calculate impact as population exposed to each class population = self.exposure.layer.get_data(scaling=True) # Get all population data that falls in each hazard class high_hazard_population = numpy.where( hazard_data == high_class, population, 0) medium_hazard_population = numpy.where( hazard_data == medium_class, population, 0) low_hazard_population = numpy.where( hazard_data == low_class, population, 0) affected_population = ( high_hazard_population + medium_hazard_population + low_hazard_population) # Carry the no data values forward to the impact layer. affected_population = numpy.where( numpy.isnan(population), numpy.nan, affected_population) affected_population = numpy.where( numpy.isnan(hazard_data), numpy.nan, affected_population) # Count totals self.total_population = int(numpy.nansum(population)) self.affected_population[ tr('Population in High hazard class areas')] = int( numpy.nansum(high_hazard_population)) self.affected_population[ tr('Population in Medium hazard class areas')] = int( numpy.nansum(medium_hazard_population)) self.affected_population[ tr('Population in Low hazard class areas')] = int( numpy.nansum(low_hazard_population)) self.unaffected_population = ( self.total_population - self.total_affected_population) # check for zero impact if self.total_affected_population == 0: message = no_population_impact_message(self.question) raise ZeroImpactException(message) self.minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] total_needs = self.total_needs impact_table = impact_summary = self.html_report() # Create style colours = [ '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] classes = create_classes(affected_population.flat[:], len(colours)) interval_classes = humanize_class(classes) style_classes = [] for i in xrange(len(colours)): style_class = dict() if i == 1: label = create_label( interval_classes[i], tr('Low Population [%i people/cell]' % classes[i])) elif i == 4: label = create_label( interval_classes[i], tr('Medium Population [%i people/cell]' % classes[i])) elif i == 7: label = create_label( interval_classes[i], tr('High Population [%i people/cell]' % classes[i])) else: label = create_label(interval_classes[i]) style_class['label'] = label style_class['quantity'] = classes[i] if i == 0: transparency = 100 else: transparency = 0 style_class['transparency'] = transparency style_class['colour'] = colours[i] style_classes.append(style_class) style_info = dict( target_field=None, style_classes=style_classes, style_type='rasterStyle') # For printing map purpose map_title = tr('Number of people affected in each class') legend_title = tr('Number of People') legend_units = tr('(people per cell)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) # Create raster object and return raster_layer = Raster( data=affected_population, projection=self.exposure.layer.get_projection(), geotransform=self.exposure.layer.get_geotransform(), name=tr('People that might %s') % ( self.impact_function_manager .get_function_title(self).lower()), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title, 'total_needs': total_needs}, style_info=style_info) self._impact = raster_layer return raster_layer
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
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
def run(self, layers=None): """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 """ self.validate() self.prepare(layers) thresholds = self.parameters['Categorical thresholds'] # 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] # Identify hazard and exposure layers hazard_layer = self.hazard # Categorised Hazard exposure_layer = self.exposure # Population Raster # Extract data as numeric arrays hazard_data = hazard_layer.get_data(nan=True) # Category no_data_warning = False if has_no_data(hazard_data): no_data_warning = True # Calculate impact as population exposed to each category exposure_data = exposure_layer.get_data(nan=True, scaling=True) if has_no_data(exposure_data): 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 total = int(numpy.nansum(exposure_data)) low_total = int(numpy.nansum(low_exposure)) medium_total = int(numpy.nansum(medium_exposure)) high_total = int(numpy.nansum(high_exposure)) total_impact = high_total + medium_total + low_total # Check for zero impact if total_impact == 0: table_body = [ self.question, TableRow( [tr('People impacted'), '%s' % format_int(total_impact)], header=True) ] message = Table(table_body).toNewlineFreeString() raise ZeroImpactException(message) # Don't show digits less than a 1000 total = population_rounding(total) total_impact = population_rounding(total_impact) low_total = population_rounding(low_total) medium_total = population_rounding(medium_total) high_total = population_rounding(high_total) minimum_needs = [ parameter.serialize() for parameter in self.parameters['minimum needs'] ] table_body = self._tabulate(high_total, low_total, medium_total, self.question, total_impact) impact_table = Table(table_body).toNewlineFreeString() table_body, total_needs = self._tabulate_notes(minimum_needs, table_body, total, total_impact, no_data_warning) impact_summary = Table(table_body).toNewlineFreeString() map_title = tr('People in each hazard areas (low, medium, high)') # 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] 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') # Create raster object and return raster_layer = Raster( data=impacted_exposure, projection=hazard_layer.get_projection(), geotransform=hazard_layer.get_geotransform(), name=tr('Population might %s') % (self.impact_function_manager.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) self._impact = raster_layer return raster_layer