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): """Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() # Hazard Zone Attribute hazard_zone_attribute = 'radius' # Parameters radii = self.parameters['distances'].value # Get parameters from layer's keywords volcano_name_attribute = self.hazard.keyword('volcano_name_field') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_point_data: message = ( 'Input hazard must be a vector point layer. I got %s ' 'with layer type %s' % ( self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Make hazard layer by buffering the point centers = self.hazard.layer.get_geometry() features = self.hazard.layer.get_data() radii_meter = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points( centers, radii_meter, hazard_zone_attribute, data_table=features) # Category names for the impact zone category_names = radii_meter self._affected_categories_volcano = radii_meter[:] category_names.append(self._not_affected_value) # Get names of volcanoes considered if volcano_name_attribute in hazard_layer.get_attribute_names(): volcano_name_list = set() for row in hazard_layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[volcano_name_attribute]) self.volcano_names = ', '.join(volcano_name_list) # Find the target field name that has no conflict with the attribute # names in the hazard layer hazard_attribute_names = hazard_layer.get_attribute_names() target_field = get_non_conflicting_attribute_name( self.target_field, hazard_attribute_names) # Run interpolation function for polygon2polygon interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, self.exposure.layer) # Extract relevant interpolated layer data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} self.affected_buildings = OrderedDict() for category in radii_meter: self.affected_buildings[category] = {} # Iterate the interpolated building layer for i in range(len(features)): hazard_value = features[i][hazard_zone_attribute] if not hazard_value: hazard_value = self._not_affected_value features[i][target_field] = hazard_value # Count affected buildings by usage type if available if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage is [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict( target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic buffered point') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic buffered point'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
from safe.engine.core import buffer_points from safe.storage.core import read_layer H = read_layer('/data_area/InaSAFE/public_data/hazard/Marapi.shp') print H.get_geometry() # Generate evacuation circle (as a polygon): radius = 3000 center = H.get_geometry()[0] Z = buffer_points(center, radius, 'Radius') Z.write_to_file('Marapi_evac_zone_%im.shp' % radius)
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): """Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Hazard Zone Attribute hazard_zone_attribute = 'radius' # Parameters radii = self.parameters['distances'].value # Get parameters from layer's keywords volcano_name_attribute = self.hazard.keyword('volcano_name_field') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_point_data: message = ( 'Input hazard must be a vector point layer. I got %s ' 'with layer type %s' % ( self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Make hazard layer by buffering the point centers = self.hazard.layer.get_geometry() features = self.hazard.layer.get_data() hazard_layer = buffer_points( centers, radii, hazard_zone_attribute, data_table=features) # Category names for the impact zone category_names = radii # In kilometers self._affected_categories_volcano = [ tr('Radius %.1f km') % key for key in radii[::]] # Get names of volcanoes considered if volcano_name_attribute in hazard_layer.get_attribute_names(): volcano_name_list = set() for row in hazard_layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[volcano_name_attribute]) self.volcano_names = ', '.join(volcano_name_list) # Find the target field name that has no conflict with the attribute # names in the hazard layer hazard_attribute_names = hazard_layer.get_attribute_names() target_field = get_non_conflicting_attribute_name( self.target_field, hazard_attribute_names) # Run interpolation function for polygon2polygon interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, self.exposure.layer) # Extract relevant interpolated layer data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} self.affected_buildings = OrderedDict() for category in radii: self.affected_buildings[category] = {} # Iterate the interpolated building layer for i in range(len(features)): hazard_value = features[i][hazard_zone_attribute] if not hazard_value: hazard_value = self._not_affected_value features[i][target_field] = hazard_value # Count affected buildings by usage type if available if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage is [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Adding 'km' affected_building_keys = self.affected_buildings.keys() for key in affected_building_keys: self.affected_buildings[tr('Radius %.1f km' % key)] = \ self.affected_buildings.pop(key) # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr('Radius %s km') % tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict( target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic buffered point') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic buffered point'), keywords=impact_layer_keywords, style_info=style_info) self._impact = impact_layer return impact_layer