def run(self): """Risk plugin for classified polygon hazard on building/structure. Counts number of building exposed to each hazard zones. :returns: Impact vector layer building exposed to each hazard zones. Table with number of buildings affected :rtype: Vector """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Value from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Retrieve the classification that is used by the hazard layer. vector_hazard_classification = self.hazard.keyword( 'vector_hazard_classification') # Get the dictionary that contains the definition of the classification vector_hazard_classification = definition(vector_hazard_classification) # Get the list classes in the classification vector_hazard_classes = vector_hazard_classification['classes'] # Initialize OrderedDict of affected buildings self.affected_buildings = OrderedDict() # Iterate over vector hazard classes for vector_hazard_class in vector_hazard_classes: # Check if the key of class exist in hazard_class_mapping if vector_hazard_class['key'] in self.hazard_class_mapping.keys(): # Replace the key with the name as we need to show the human # friendly name in the report. self.hazard_class_mapping[vector_hazard_class['name']] = \ self.hazard_class_mapping.pop(vector_hazard_class['key']) # Adding the class name as a key in affected_building self.affected_buildings[vector_hazard_class['name']] = {} hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex( self.hazard_class_attribute) # Check if hazard_zone_attribute exists in hazard_layer if hazard_zone_attribute_index < 0: message = ( 'Hazard data %s does not contain expected attribute %s ' % (self.hazard.layer.name(), self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Hazard zone categories from hazard layer unique_values = self.hazard.layer.uniqueValues( hazard_zone_attribute_index) # Values might be integer or float, we should have unicode. #2626 self.hazard_zones = [get_unicode(val) for val in unique_values] self.buildings = {} wgs84_extent = QgsRectangle( self.requested_extent[0], self.requested_extent[1], self.requested_extent[2], self.requested_extent[3]) # Run interpolation function for polygon2polygon interpolated_layer = interpolate_polygon_polygon( self.hazard.layer, self.exposure.layer, wgs84_extent) new_field = QgsField(self.target_field, QVariant.String) interpolated_layer.dataProvider().addAttributes([new_field]) interpolated_layer.updateFields() attribute_names = [ field.name() for field in interpolated_layer.pendingFields()] target_field_index = interpolated_layer.fieldNameIndex( self.target_field) changed_values = {} if interpolated_layer.featureCount() < 1: raise ZeroImpactException() # Extract relevant interpolated data for feature in interpolated_layer.getFeatures(): # Get the hazard value based on the value mapping in keyword hazard_value = get_key_for_value( feature[self.hazard_class_attribute], self.hazard_class_mapping) if not hazard_value: hazard_value = self._not_affected_value changed_values[feature.id()] = {target_field_index: hazard_value} if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = feature[self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, feature) if usage is None: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.hazard_class_mapping.keys(): self.affected_buildings[category][usage] = OrderedDict( [(tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.hazard_class_mapping.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 interpolated_layer.dataProvider().changeAttributeValues(changed_values) # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style categories = self.affected_buildings.keys() categories.append(self._not_affected_value) colours = color_ramp(len(categories)) style_classes = [] i = 0 for hazard_zone in self.affected_buildings.keys(): style_class = dict() style_class['label'] = tr(hazard_zone) style_class['transparency'] = 0 style_class['value'] = hazard_zone style_class['size'] = 1 style_class['colour'] = colours[i] style_classes.append(style_class) i += 1 # Override style info with new classes and name style_info = dict( target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol' ) # For printing map purpose map_title = tr('Buildings affected by each hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr( 'Thousand separator is represented by %s' % get_thousand_separator()) extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=interpolated_layer, name=tr('Buildings affected by each hazard zone'), keywords=impact_layer_keywords, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self): """Risk plugin for volcano hazard on building/structure. Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.name_attribute = self.hazard.keyword('volcano_name_field') self.hazard_class_mapping = self.hazard.keyword('value_map') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_polygon_data: message = ( 'Input hazard must be a polygon. I got %s with ' 'layer type %s' % (self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Check if hazard_zone_attribute exists in hazard_layer if (self.hazard_class_attribute not in self.hazard.layer.get_attribute_names()): message = ( 'Hazard data %s did not contain expected attribute %s ' % (self.hazard.name, self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Get names of volcanoes considered if self.name_attribute in self.hazard.layer.get_attribute_names(): volcano_name_list = set() for row in self.hazard.layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[self.name_attribute]) self.volcano_names = ', '.join(volcano_name_list) else: self.volcano_names = tr('Not specified in data') # Retrieve the classification that is used by the hazard layer. vector_hazard_classification = self.hazard.keyword( 'vector_hazard_classification') # Get the dictionary that contains the definition of the classification vector_hazard_classification = definition(vector_hazard_classification) # Get the list classes in the classification vector_hazard_classes = vector_hazard_classification['classes'] # Initialize OrderedDict of affected buildings self.affected_buildings = OrderedDict() # Iterate over vector hazard classes for vector_hazard_class in vector_hazard_classes: # Check if the key of class exist in hazard_class_mapping if vector_hazard_class['key'] in self.hazard_class_mapping.keys(): # Replace the key with the name as we need to show the human # friendly name in the report. self.hazard_class_mapping[vector_hazard_class['name']] = \ self.hazard_class_mapping.pop(vector_hazard_class['key']) # Adding the class name as a key in affected_building self.affected_buildings[vector_hazard_class['name']] = {} # Run interpolation function for polygon2raster interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} for i in range(len(features)): # Get the hazard value based on the value mapping in keyword hazard_value = get_key_for_value( features[i][self.hazard_class_attribute], self.hazard_class_mapping) if not hazard_value: hazard_value = self._not_affected_value features[i][self.target_field] = get_string(hazard_value) if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage in [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(self.affected_buildings.keys())] style_classes = [] i = 0 for category_name in self.affected_buildings.keys(): style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(self.affected_buildings.keys()): i = len(self.affected_buildings.keys()) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic hazard zone'), keywords=impact_layer_keywords, style_info=style_info ) self._impact = impact_layer return impact_layer
def run(self): """Tsunami raster impact to buildings (e.g. from Open Street Map).""" self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Thresholds for tsunami hazard zone breakdown. low_max = self.parameters['low_threshold'].value medium_max = self.parameters['medium_threshold'].value high_max = self.parameters['high_threshold'].value # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=self.target_field) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (self.hazard_classes[0], {}), (self.hazard_classes[1], {}), (self.hazard_classes[2], {}), (self.hazard_classes[3], {}), (self.hazard_classes[4], {}) ]) categories = self.affected_buildings.keys() for i in range(total_features): # Get the interpolated depth water_depth = float(features[i][self.target_field]) if water_depth <= 0: inundated_status = 0 elif 0 < water_depth <= low_max: inundated_status = 1 # low elif low_max < water_depth <= medium_max: inundated_status = 2 # medium elif medium_max < water_depth <= high_max: inundated_status = 3 # high elif high_max < water_depth: inundated_status = 4 # very high # If not a number or a value beside real number. else: inundated_status = 0 # Count affected buildings by usage type if available if (structure_class_field in attribute_names and structure_class_field): usage = features[i].get(structure_class_field, None) else: usage = get_osm_building_usage( attribute_names, features[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) # Count all buildings by type self.buildings[usage] += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status category = categories[inundated_status] self.affected_buildings[category][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_table = impact_summary = self.html_report() # For printing map purpose map_title = tr('Inundated buildings') legend_title = tr('Inundated structure status') legend_units = tr('(low, medium, high, and very high)') style_classes = [ dict( label=self.hazard_classes[0] + ': 0 m', value=0, colour='#00FF00', transparency=0, size=1 ), dict( label=self.hazard_classes[1] + ': 0.1 - %.1f m' % low_max, value=1, colour='#FFFF00', transparency=0, size=1 ), dict( label=self.hazard_classes[2] + ': %.1f - %.1f m' % ( low_max + 0.1, medium_max), value=2, colour='#FFB700', transparency=0, size=1 ), dict( label=self.hazard_classes[3] + ': %.1f - %.1f m' % ( medium_max + 0.1, high_max), value=3, colour='#FF6F00', transparency=0, size=1 ), dict( label=self.hazard_classes[4] + ' > %.1f m' % high_max, value=4, colour='#FF0000', transparency=0, size=1 ), ] style_info = dict( target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_title': legend_title, 'legend_units': legend_units, 'buildings_total': total_features, 'buildings_affected': self.total_affected_buildings } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) vector_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords=impact_layer_keywords, style_info=style_info) # Create vector layer and return self._impact = vector_layer return vector_layer
def run(self, layers=None): """Earthquake impact to buildings (e.g. from OpenStreetMap). :param layers: All the input layers (Hazard Layer and Exposure Layer) """ self.validate() self.prepare(layers) LOGGER.debug('Running earthquake building impact') # merely initialize building_value = 0 contents_value = 0 # Thresholds for mmi breakdown. t0 = self.parameters['low_threshold'] t1 = self.parameters['medium_threshold'] t2 = self.parameters['high_threshold'] # Class Attribute and Label. class_1 = {'label': tr('Low'), 'class': 1} class_2 = {'label': tr('Medium'), 'class': 2} class_3 = {'label': tr('High'), 'class': 3} # Extract data hazard_layer = self.hazard # Depth exposure_layer = self.exposure # Building locations # Define attribute name for hazard levels. hazard_attribute = 'mmi' # Determine if exposure data have NEXIS attributes. attribute_names = exposure_layer.get_attribute_names() if ( 'FLOOR_AREA' in attribute_names and 'BUILDING_C' in attribute_names and 'CONTENTS_C' in attribute_names): self.is_nexis = True else: self.is_nexis = False # Interpolate hazard level to building locations. interpolate_result = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute ) # Extract relevant exposure data # attribute_names = interpolate_result.get_attribute_names() attributes = interpolate_result.get_data() interpolate_size = len(interpolate_result) # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (tr('High'), {}), (tr('Medium'), {}), (tr('Low'), {}) ]) for i in range(interpolate_size): # Classify building according to shake level # and calculate dollar losses if self.is_nexis: try: area = float(attributes[i]['FLOOR_AREA']) except (ValueError, KeyError): # print 'Got area', attributes[i]['FLOOR_AREA'] area = 0.0 try: building_value_density = float(attributes[i]['BUILDING_C']) except (ValueError, KeyError): # print 'Got bld value', attributes[i]['BUILDING_C'] building_value_density = 0.0 try: contents_value_density = float(attributes[i]['CONTENTS_C']) except (ValueError, KeyError): # print 'Got cont value', attributes[i]['CONTENTS_C'] contents_value_density = 0.0 building_value = building_value_density * area contents_value = contents_value_density * area usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): if self.is_nexis: self.affected_buildings[category][usage] = OrderedDict( [ (tr('Buildings Affected'), 0), (tr('Buildings value ($M)'), 0), (tr('Contents value ($M)'), 0)]) else: self.affected_buildings[category][usage] = OrderedDict( [ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 try: mmi = float(attributes[i][hazard_attribute]) # MMI except TypeError: mmi = 0.0 if t0 <= mmi < t1: cls = 1 category = tr('Low') elif t1 <= mmi < t2: cls = 2 category = tr('Medium') elif t2 <= mmi: cls = 3 category = tr('High') else: # Not reported for less than level t0 continue attributes[i][self.target_field] = cls self.affected_buildings[ category][usage][tr('Buildings Affected')] += 1 if self.is_nexis: self.affected_buildings[category][usage][ tr('Buildings value ($M)')] += building_value / 1000000.0 self.affected_buildings[category][usage][ tr('Contents value ($M)')] += contents_value / 1000000.0 # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() impact_table = impact_summary = self.generate_html_report() # Create style style_classes = [dict(label=class_1['label'], value=class_1['class'], colour='#ffff00', transparency=1), dict(label=class_2['label'], value=class_2['class'], colour='#ffaa00', transparency=1), dict(label=class_3['label'], value=class_3['class'], colour='#ff0000', transparency=1)] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Building affected by earthquake') legend_notes = tr('The level of the impact is according to the ' 'threshold the user input.') legend_units = tr('(mmi)') legend_title = tr('Impact level') # Create vector layer and return result_layer = Vector( data=attributes, projection=interpolate_result.get_projection(), geometry=interpolate_result.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title, 'target_field': self.target_field, 'statistics_type': self.statistics_type, 'statistics_classes': self.statistics_classes}, style_info=style_info) msg = 'Created vector layer %s' % str(result_layer) LOGGER.debug(msg) self._impact = result_layer return result_layer
def run(self): """Risk plugin for volcano hazard on building/structure. Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.name_attribute = self.hazard.keyword('volcano_name_field') self.hazard_class_mapping = self.hazard.keyword('value_map') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_polygon_data: message = ( 'Input hazard must be a polygon. I got %s with ' 'layer type %s' % (self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Check if hazard_zone_attribute exists in hazard_layer if (self.hazard_class_attribute not in self.hazard.layer.get_attribute_names()): message = ( 'Hazard data %s did not contain expected attribute %s ' % (self.hazard.name, self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Get names of volcanoes considered if self.name_attribute in self.hazard.layer.get_attribute_names(): volcano_name_list = set() for row in self.hazard.layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[self.name_attribute]) self.volcano_names = ', '.join(volcano_name_list) else: self.volcano_names = tr('Not specified in data') # Retrieve the classification that is used by the hazard layer. vector_hazard_classification = self.hazard.keyword( 'vector_hazard_classification') # Get the dictionary that contains the definition of the classification vector_hazard_classification = definition(vector_hazard_classification) # Get the list classes in the classification vector_hazard_classes = vector_hazard_classification['classes'] # Initialize OrderedDict of affected buildings self.affected_buildings = OrderedDict() # Iterate over vector hazard classes for vector_hazard_class in vector_hazard_classes: # Check if the key of class exist in hazard_class_mapping if vector_hazard_class['key'] in self.hazard_class_mapping.keys(): # Replace the key with the name as we need to show the human # friendly name in the report. self.hazard_class_mapping[vector_hazard_class['name']] = \ self.hazard_class_mapping.pop(vector_hazard_class['key']) # Adding the class name as a key in affected_building self.affected_buildings[vector_hazard_class['name']] = {} # Run interpolation function for polygon2raster interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} for i in range(len(features)): # Get the hazard value based on the value mapping in keyword hazard_value = get_key_for_value( features[i][self.hazard_class_attribute], self.hazard_class_mapping) if not hazard_value: hazard_value = self._not_affected_value features[i][self.target_field] = get_string(hazard_value) if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage in [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][tr( 'Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style colours = [ '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000' ] colours = colours[::-1] # flip colours = colours[:len(self.affected_buildings.keys())] style_classes = [] i = 0 for category_name in self.affected_buildings.keys(): style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(self.affected_buildings.keys()): i = len(self.affected_buildings.keys()) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic hazard zone'), keywords=impact_layer_keywords, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Classified hazard impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard: Classified Hazard layer * exposure: Vector layer of structure data on the same grid as hazard """ self.validate() self.prepare(layers) # The 3 classes low_t = self.parameters['low_hazard_class'] medium_t = self.parameters['medium_hazard_class'] high_t = self.parameters['high_hazard_class'] # Extract data hazard = self.hazard # Classified Hazard exposure = self.exposure # Building locations # Determine attribute name for hazard levels if hazard.is_raster: hazard_attribute = 'level' else: hazard_attribute = None interpolated_result = assign_hazard_values_to_exposure_data( hazard, exposure, attribute_name=hazard_attribute, mode='constant') # Extract relevant exposure data attribute_names = interpolated_result.get_attribute_names() attributes = interpolated_result.get_data() buildings_total = len(interpolated_result) # Calculate building impact self.buildings = {} self.affected_buildings = OrderedDict([(tr('High Hazard Class'), {}), (tr('Medium Hazard Class'), {}), (tr('Low Hazard Class'), {})]) for i in range(buildings_total): usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) # Count all buildings by type self.buildings[usage] += 1 attributes[i][self.target_field] = 0 attributes[i][self.affected_field] = 0 level = float(attributes[i]['level']) level = float(numpy_round(level)) if level == high_t: impact_level = tr('High Hazard Class') elif level == medium_t: impact_level = tr('Medium Hazard Class') elif level == low_t: impact_level = tr('Low Hazard Class') else: continue # Add calculated impact to existing attributes attributes[i][self.target_field] = { tr('High Hazard Class'): 3, tr('Medium Hazard Class'): 2, tr('Low Hazard Class'): 1 }[impact_level] attributes[i][self.affected_field] = 1 # Count affected buildings by type self.affected_buildings[impact_level][usage][tr( 'Buildings Affected')] += 1 # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() # Create style style_classes = [ dict(label=tr('High'), value=3, colour='#F31A1C', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Medium'), value=2, colour='#F4A442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Low'), value=1, colour='#EBF442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Not Affected'), value=None, colour='#1EFC7C', transparency=0, size=2, border_color='#969696', border_width=0.2) ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_table = impact_summary = self.generate_html_report() # For printing map purpose map_title = tr('Buildings affected') legend_units = tr('(Low, Medium, High)') legend_title = tr('Structure inundated status') # Create vector layer and return vector_layer = Vector(data=attributes, projection=exposure.get_projection(), geometry=exposure.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.affected_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': buildings_total, 'buildings_affected': self.total_affected_buildings }, style_info=style_info) self._impact = vector_layer return vector_layer
def run(self): """Risk plugin for classified polygon hazard on building/structure. Counts number of building exposed to each hazard zones. :returns: Impact vector layer building exposed to each hazard zones. Table with number of buildings affected :rtype: Vector """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Value from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Retrieve the classification that is used by the hazard layer. vector_hazard_classification = self.hazard.keyword( 'vector_hazard_classification') # Get the dictionary that contains the definition of the classification vector_hazard_classification = definition(vector_hazard_classification) # Get the list classes in the classification vector_hazard_classes = vector_hazard_classification['classes'] # Initialize OrderedDict of affected buildings self.affected_buildings = OrderedDict() # Iterate over vector hazard classes for vector_hazard_class in vector_hazard_classes: # Check if the key of class exist in hazard_class_mapping if vector_hazard_class['key'] in self.hazard_class_mapping.keys(): # Replace the key with the name as we need to show the human # friendly name in the report. self.hazard_class_mapping[vector_hazard_class['name']] = \ self.hazard_class_mapping.pop(vector_hazard_class['key']) # Adding the class name as a key in affected_building self.affected_buildings[vector_hazard_class['name']] = {} hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex( self.hazard_class_attribute) # Check if hazard_zone_attribute exists in hazard_layer if hazard_zone_attribute_index < 0: message = ( 'Hazard data %s does not contain expected attribute %s ' % (self.hazard.layer.name(), self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Hazard zone categories from hazard layer unique_values = self.hazard.layer.uniqueValues( hazard_zone_attribute_index) # Values might be integer or float, we should have unicode. #2626 self.hazard_zones = [get_unicode(val) for val in unique_values] self.buildings = {} wgs84_extent = QgsRectangle(self.requested_extent[0], self.requested_extent[1], self.requested_extent[2], self.requested_extent[3]) # Run interpolation function for polygon2polygon interpolated_layer = interpolate_polygon_polygon( self.hazard.layer, self.exposure.layer, wgs84_extent) new_field = QgsField(self.target_field, QVariant.String) interpolated_layer.dataProvider().addAttributes([new_field]) interpolated_layer.updateFields() attribute_names = [ field.name() for field in interpolated_layer.pendingFields() ] target_field_index = interpolated_layer.fieldNameIndex( self.target_field) changed_values = {} if interpolated_layer.featureCount() < 1: raise ZeroImpactException() # Extract relevant interpolated data for feature in interpolated_layer.getFeatures(): # Get the hazard value based on the value mapping in keyword hazard_value = get_key_for_value( feature[self.hazard_class_attribute], self.hazard_class_mapping) if not hazard_value: hazard_value = self._not_affected_value changed_values[feature.id()] = {target_field_index: hazard_value} if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = feature[self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, feature) if usage is None: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.hazard_class_mapping.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) self.buildings[usage] += 1 if hazard_value in self.hazard_class_mapping.keys(): self.affected_buildings[hazard_value][usage][tr( 'Buildings Affected')] += 1 interpolated_layer.dataProvider().changeAttributeValues(changed_values) # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style categories = self.affected_buildings.keys() categories.append(self._not_affected_value) colours = color_ramp(len(categories)) style_classes = [] i = 0 for hazard_zone in self.affected_buildings.keys(): style_class = dict() style_class['label'] = tr(hazard_zone) style_class['transparency'] = 0 style_class['value'] = hazard_zone style_class['size'] = 1 style_class['colour'] = colours[i] style_classes.append(style_class) i += 1 # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by each hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=interpolated_layer, name=tr('Buildings affected by each hazard zone'), keywords=impact_layer_keywords, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Risk plugin for classified polygon hazard on building/structure. Counts number of building exposed to each hazard zones. :param layers: List of layers expected to contain. * hazard_layer: Hazard layer * exposure_layer: Vector layer of structure data on the same grid as hazard_layer :returns: Map of building exposed to each hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare(layers) # Target Field target_field = 'zone' # Not affected string in the target field not_affected_value = 'Not Affected' # Parameters hazard_zone_attribute = self.parameters['hazard zone attribute'] # Identify hazard and exposure layers hazard_layer = self.hazard exposure_layer = self.exposure # Input checks if not hazard_layer.is_polygon_data: message = ( 'Input hazard must be a polygon. I got %s with ' 'layer type %s' % (hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(message) # Check if hazard_zone_attribute exists in hazard_layer if hazard_zone_attribute not in hazard_layer.get_attribute_names(): message = ( 'Hazard data %s does not contain expected attribute %s ' % (hazard_layer.get_name(), hazard_zone_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Find the target field name that has no conflict with default # target attribute_names = hazard_layer.get_attribute_names() target_field = get_non_conflicting_attribute_name( target_field, attribute_names) # Hazard zone categories from hazard layer self.hazard_zones = list( set(hazard_layer.get_data(hazard_zone_attribute))) self.buildings = {} self.affected_buildings = OrderedDict() for hazard_zone in self.hazard_zones: self.affected_buildings[hazard_zone] = {} # Run interpolation function for polygon2polygon interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=None) # Extract relevant interpolated data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() for i in range(len(features)): hazard_value = features[i][hazard_zone_attribute] if not hazard_value: hazard_value = not_affected_value features[i][target_field] = hazard_value usage = get_osm_building_usage(attribute_names, features[i]) if usage is None: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict( [(tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.generate_html_report() # Create style categories = self.hazard_zones categories.append(not_affected_value) colours = color_ramp(len(categories)) style_classes = [] i = 0 for hazard_zone in self.hazard_zones: style_class = dict() style_class['label'] = tr(hazard_zone) style_class['transparency'] = 0 style_class['value'] = hazard_zone style_class['size'] = 1 style_class['colour'] = colours[i] style_classes.append(style_class) i += 1 # Override style info with new classes and name style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by each hazard zone') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) legend_units = tr('(building)') legend_title = tr('Building count') # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by each hazard zone'), keywords={'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Flood impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard_layer: Hazard raster layer of flood * exposure_layer: Vector layer of structure data on the same grid as hazard_layer """ self.validate() self.prepare(layers) threshold = self.parameters['threshold [m]'] # Flood threshold [m] verify(isinstance(threshold, float), 'Expected thresholds to be a float. Got %s' % str(threshold)) # Extract data hazard_layer = self.hazard # Depth exposure_layer = self.exposure # Building locations # Determine attribute name for hazard levels hazard_attribute = 'depth' # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (tr('Number Inundated'), {}), (tr('Number of Wet Buildings'), {}), (tr('Number of Dry Buildings'), {}) ]) for i in range(total_features): # Get the interpolated depth water_depth = float(features[i]['depth']) if water_depth <= 0: inundated_status = 0 # dry elif water_depth >= threshold: inundated_status = 1 # inundated else: inundated_status = 2 # wet # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) # Count all buildings by type self.buildings[usage] += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status category = [ tr('Number of Dry Buildings'), tr('Number of Wet Buildings'), tr('Number Inundated')][inundated_status] self.affected_buildings[category][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_table = impact_summary = self.generate_html_report() # Prepare impact layer map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') style_classes = [ dict( label=tr('Dry (<= 0 m)'), value=0, colour='#1EFC7C', transparency=0, size=1 ), dict( label=tr('Wet (0 m - %.1f m)') % threshold, value=2, colour='#FF9900', transparency=0, size=1 ), dict( label=tr('Inundated (>= %.1f m)') % threshold, value=1, colour='#F31A1C', transparency=0, size=1 )] legend_units = tr('(inundated, wet, or dry)') style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Create vector layer and return vector_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': total_features, 'buildings_affected': self.total_affected_buildings}, style_info=style_info) self._impact = vector_layer return vector_layer
def run(self): """Risk plugin for volcano hazard on building/structure. Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') name_attribute = self.hazard.keyword('volcano_name_field') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_polygon_data: message = ( 'Input hazard must be a polygon. I got %s with ' 'layer type %s' % (self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Check if hazard_zone_attribute exists in hazard_layer if (self.hazard_class_attribute not in self.hazard.layer.get_attribute_names()): message = ( 'Hazard data %s did not contain expected attribute %s ' % (self.hazard.name, self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Get names of volcanoes considered if name_attribute in self.hazard.layer.get_attribute_names(): volcano_name_list = set() for row in self.hazard.layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[name_attribute]) self.volcano_names = ', '.join(volcano_name_list) else: self.volcano_names = tr('Not specified in data') # Run interpolation function for polygon2raster interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() # Hazard zone categories from hazard layer hazard_zone_categories = list( set(self.hazard.layer.get_data(self.hazard_class_attribute))) self.buildings = {} self.affected_buildings = OrderedDict() for hazard_category in hazard_zone_categories: self.affected_buildings[hazard_category] = {} for i in range(len(features)): hazard_value = features[i][self.hazard_class_attribute] if not hazard_value: hazard_value = self._not_affected_value features[i][self.target_field] = hazard_value if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage in [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() category_names = hazard_zone_categories category_names.append(self._not_affected_value) # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic hazard zone'), keywords={'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers): """Flood impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard_layer: Hazard raster layer of flood * exposure_layer: Vector layer of structure data on the same grid as hazard_layer """ threshold = self.parameters['threshold [m]'] # Flood threshold [m] verify(isinstance(threshold, float), 'Expected thresholds to be a float. Got %s' % str(threshold)) # Extract data hazard_layer = get_hazard_layer(layers) # Depth exposure_layer = get_exposure_layer(layers) # Building locations question = get_question(hazard_layer.get_name(), exposure_layer.get_name(), self) # Determine attribute name for hazard levels mode = 'grid' hazard_attribute = 'depth' # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) buildings = {} # The number of affected buildings affected_count = 0 # The variable for grid mode inundated_count = 0 wet_count = 0 dry_count = 0 inundated_buildings = {} wet_buildings = {} dry_buildings = {} # The variable for regions mode affected_buildings = {} for i in range(total_features): # Get the interpolated depth water_depth = float(features[i]['depth']) if water_depth <= 0: inundated_status = 0 # dry elif water_depth >= threshold: inundated_status = 1 # inundated else: inundated_status = 2 # wet # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is not None and usage != 0: key = usage else: key = 'unknown' if key not in buildings: buildings[key] = 0 inundated_buildings[key] = 0 wet_buildings[key] = 0 dry_buildings[key] = 0 # Count all buildings by type buildings[key] += 1 if inundated_status is 0: # Count dry buildings by type dry_buildings[key] += 1 # Count total dry buildings dry_count += 1 if inundated_status is 1: # Count inundated buildings by type inundated_buildings[key] += 1 # Count total dry buildings inundated_count += 1 if inundated_status is 2: # Count wet buildings by type wet_buildings[key] += 1 # Count total wet buildings wet_count += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status affected_count = inundated_count + wet_count # Lump small entries and 'unknown' into 'other' category for usage in buildings.keys(): x = buildings[usage] if x < 25 or usage == 'unknown': if 'other' not in buildings: buildings['other'] = 0 if mode == 'grid': inundated_buildings['other'] = 0 wet_buildings['other'] = 0 dry_buildings['other'] = 0 elif mode == 'regions': affected_buildings['other'] = 0 buildings['other'] += x if mode == 'grid': inundated_buildings['other'] += inundated_buildings[usage] wet_buildings['other'] += wet_buildings[usage] dry_buildings['other'] += dry_buildings[usage] del buildings[usage] del inundated_buildings[usage] del wet_buildings[usage] del dry_buildings[usage] elif mode == 'regions': affected_buildings['other'] += affected_buildings[usage] del buildings[usage] del affected_buildings[usage] # Generate simple impact report table_body = [ question, TableRow([ tr('Building type'), tr('Number Inundated'), tr('Number of Wet Buildings'), tr('Number of Dry Buildings'), tr('Total') ], header=True), TableRow([ tr('All'), format_int(inundated_count), format_int(wet_count), format_int(dry_count), format_int(total_features) ]) ] school_closed = 0 hospital_closed = 0 # Generate break down by building usage type if available list_type_attribute = [ 'TYPE', 'type', 'amenity', 'building_t', 'office', 'tourism', 'leisure', 'building' ] intersect_type = set(attribute_names) & set(list_type_attribute) if len(intersect_type) > 0: # Make list of building types building_list = [] for usage in buildings: building_type = usage.replace('_', ' ') # Lookup internationalised value if available building_type = tr(building_type) building_list.append([ building_type.capitalize(), format_int(inundated_buildings[usage]), format_int(wet_buildings[usage]), format_int(dry_buildings[usage]), format_int(buildings[usage]) ]) if usage.lower() == 'school': school_closed = 0 school_closed += inundated_buildings[usage] school_closed += wet_buildings[usage] if usage.lower() == 'hospital': hospital_closed = 0 hospital_closed += inundated_buildings[usage] hospital_closed += wet_buildings[usage] # Sort alphabetically building_list.sort() table_body.append( TableRow(tr('Breakdown by building type'), header=True)) for row in building_list: s = TableRow(row) table_body.append(s) # Action Checklist Section table_body.append(TableRow(tr('Action Checklist:'), header=True)) table_body.append( TableRow(tr('Are the critical facilities still open?'))) table_body.append( TableRow( tr('Which structures have warning capacity (eg. sirens, speakers, ' 'etc.)?'))) table_body.append( TableRow(tr('Which buildings will be evacuation centres?'))) table_body.append( TableRow(tr('Where will we locate the operations centre?'))) table_body.append( TableRow( tr('Where will we locate warehouse and/or distribution centres?' ))) if school_closed > 0: table_body.append( TableRow( tr('Where will the students from the %s closed schools go to ' 'study?') % format_int(school_closed))) if hospital_closed > 0: table_body.append( TableRow( tr('Where will the patients from the %s closed hospitals go ' 'for treatment and how will we transport them?') % format_int(hospital_closed))) # Notes Section table_body.append(TableRow(tr('Notes'), header=True)) table_body.append( TableRow( tr('Buildings are said to be inundated when flood levels ' 'exceed %.1f m') % threshold)) table_body.append( TableRow( tr('Buildings are said to be wet when flood levels ' 'are greater than 0 m but less than %.1f m') % threshold)) table_body.append( TableRow( tr('Buildings are said to be dry when flood levels ' 'are less than 0 m'))) table_body.append( TableRow( tr('Buildings are said to be closed if they are inundated or ' 'wet'))) table_body.append( TableRow(tr('Buildings are said to be open if they are dry'))) # Result impact_summary = Table(table_body).toNewlineFreeString() impact_table = impact_summary # Prepare impact layer map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') style_classes = [ dict(label=tr('Dry (<= 0 m)'), value=0, colour='#1EFC7C', transparency=0, size=1), dict(label=tr('Wet (0 m - %.1f m)') % threshold, value=2, colour='#FF9900', transparency=0, size=1), dict(label=tr('Inundated (>= %.1f m)') % threshold, value=1, colour='#F31A1C', transparency=0, size=1) ] legend_units = tr('(inundated, wet, or dry)') style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Create vector layer and return vector_layer = Vector(data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': total_features, 'buildings_affected': affected_count }, style_info=style_info) return vector_layer
def run(self): """Classified hazard impact to buildings (e.g. from Open Street Map). """ self.validate() self.prepare() self.provenance.append_step("Calculating Step", "Impact function is calculating the impact.") # Value from layer's keywords # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword("structure_class_field") except KeywordNotFoundError: structure_class_field = None # The 3 classes categorical_hazards = self.parameters["Categorical hazards"].value low_t = categorical_hazards[0].value medium_t = categorical_hazards[1].value high_t = categorical_hazards[2].value # Determine attribute name for hazard levels if self.hazard.layer.is_raster: hazard_attribute = "level" else: hazard_attribute = None interpolated_result = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute, mode="constant" ) # Extract relevant exposure data attribute_names = interpolated_result.get_attribute_names() attributes = interpolated_result.get_data() buildings_total = len(interpolated_result) # Calculate building impact self.buildings = {} self.affected_buildings = OrderedDict( [(tr("High Hazard Class"), {}), (tr("Medium Hazard Class"), {}), (tr("Low Hazard Class"), {})] ) for i in range(buildings_total): if structure_class_field and structure_class_field in attribute_names: usage = attributes[i][structure_class_field] else: usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = "unknown" if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([(tr("Buildings Affected"), 0)]) # Count all buildings by type self.buildings[usage] += 1 attributes[i][self.target_field] = 0 attributes[i][self.affected_field] = 0 level = float(attributes[i]["level"]) level = float(numpy_round(level)) if level == high_t: impact_level = tr("High Hazard Class") elif level == medium_t: impact_level = tr("Medium Hazard Class") elif level == low_t: impact_level = tr("Low Hazard Class") else: continue # Add calculated impact to existing attributes attributes[i][self.target_field] = { tr("High Hazard Class"): 3, tr("Medium Hazard Class"): 2, tr("Low Hazard Class"): 1, }[impact_level] attributes[i][self.affected_field] = 1 # Count affected buildings by type self.affected_buildings[impact_level][usage][tr("Buildings Affected")] += 1 # Consolidate the small building usage groups < 25 to other # Building threshold #2468 postprocessors = self.parameters["postprocessors"] building_postprocessors = postprocessors["BuildingType"][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Create style style_classes = [ dict( label=tr("High"), value=3, colour="#F31A1C", transparency=0, size=2, border_color="#969696", border_width=0.2, ), dict( label=tr("Medium"), value=2, colour="#F4A442", transparency=0, size=2, border_color="#969696", border_width=0.2, ), dict( label=tr("Low"), value=1, colour="#EBF442", transparency=0, size=2, border_color="#969696", border_width=0.2, ), dict( label=tr("Not Affected"), value=None, colour="#1EFC7C", transparency=0, size=2, border_color="#969696", border_width=0.2, ), ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type="categorizedSymbol") impact_table = impact_summary = self.html_report() # For printing map purpose map_title = tr("Buildings affected") legend_title = tr("Structure inundated status") legend_units = tr("(Low, Medium, High)") extra_keywords = { "impact_summary": impact_summary, "impact_table": impact_table, "target_field": self.affected_field, "map_title": map_title, "legend_units": legend_units, "legend_title": legend_title, "buildings_total": buildings_total, "buildings_affected": self.total_affected_buildings, } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return vector_layer = Vector( data=attributes, projection=self.exposure.layer.get_projection(), geometry=self.exposure.layer.get_geometry(), name=tr("Estimated buildings affected"), keywords=impact_layer_keywords, style_info=style_info, ) self._impact = vector_layer return vector_layer
def run(self): """Earthquake impact to buildings (e.g. from OpenStreetMap).""" self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') LOGGER.debug('Running earthquake building impact') # merely initialize building_value = 0 contents_value = 0 # Thresholds for mmi breakdown. t0 = self.parameters['low_threshold'].value t1 = self.parameters['medium_threshold'].value t2 = self.parameters['high_threshold'].value # Class Attribute and Label. class_1 = {'label': tr('Low'), 'class': 1} class_2 = {'label': tr('Medium'), 'class': 2} class_3 = {'label': tr('High'), 'class': 3} # Define attribute name for hazard levels. hazard_attribute = 'mmi' # Determine if exposure data have NEXIS attributes. attribute_names = self.exposure.layer.get_attribute_names() if ('FLOOR_AREA' in attribute_names and 'BUILDING_C' in attribute_names and 'CONTENTS_C' in attribute_names): self.is_nexis = True else: self.is_nexis = False # Interpolate hazard level to building locations. interpolate_result = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute) # Extract relevant exposure data # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None attributes = interpolate_result.get_data() interpolate_size = len(interpolate_result) # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (tr('High'), {}), (tr('Medium'), {}), (tr('Low'), {}), ]) removed = [] for i in range(interpolate_size): # Classify building according to shake level # and calculate dollar losses if self.is_nexis: try: area = float(attributes[i]['FLOOR_AREA']) except (ValueError, KeyError): # print 'Got area', attributes[i]['FLOOR_AREA'] area = 0.0 try: building_value_density = float(attributes[i]['BUILDING_C']) except (ValueError, KeyError): # print 'Got bld value', attributes[i]['BUILDING_C'] building_value_density = 0.0 try: contents_value_density = float(attributes[i]['CONTENTS_C']) except (ValueError, KeyError): # print 'Got cont value', attributes[i]['CONTENTS_C'] contents_value_density = 0.0 building_value = building_value_density * area contents_value = contents_value_density * area if (structure_class_field in attribute_names and structure_class_field): usage = attributes[i].get(structure_class_field, None) else: usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): if self.is_nexis: self.affected_buildings[category][usage] = OrderedDict( [(tr('Buildings Affected'), 0), (tr('Buildings value ($M)'), 0), (tr('Contents value ($M)'), 0)]) else: self.affected_buildings[category][usage] = \ OrderedDict([(tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 try: mmi = float(attributes[i][hazard_attribute]) # MMI except TypeError: mmi = 0.0 if t0 <= mmi < t1: cls = 1 category = tr('Low') elif t1 <= mmi < t2: cls = 2 category = tr('Medium') elif t2 <= mmi: cls = 3 category = tr('High') else: # Not reported for less than level t0 continue attributes[i][self.target_field] = cls self.affected_buildings[category][usage][tr( 'Buildings Affected')] += 1 if self.is_nexis: self.affected_buildings[category][usage][tr( 'Buildings value ($M)')] += building_value / 1000000.0 self.affected_buildings[category][usage][tr( 'Contents value ($M)')] += contents_value / 1000000.0 # remove un-categorized element removed.reverse() geometry = interpolate_result.get_geometry() for i in range(0, len(removed)): del attributes[removed[i]] del geometry[removed[i]] if len(attributes) < 1: raise ZeroImpactException() # Consolidate the small building usage groups < 25 to other # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() impact_table = impact_summary = self.html_report() # Create style style_classes = [ dict(label=class_1['label'], value=class_1['class'], colour='#ffff00', transparency=1), dict(label=class_2['label'], value=class_2['class'], colour='#ffaa00', transparency=1), dict(label=class_3['label'], value=class_3['class'], colour='#ff0000', transparency=1) ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Building affected by earthquake') legend_notes = tr( 'The level of the impact is according to the threshold the user ' 'input.') legend_units = tr('(mmi)') legend_title = tr('Impact level') extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title, 'target_field': self.target_field, } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return result_layer = Vector(data=attributes, projection=interpolate_result.get_projection(), geometry=geometry, name=tr('Estimated buildings affected'), keywords=impact_layer_keywords, style_info=style_info) msg = 'Created vector layer %s' % str(result_layer) LOGGER.debug(msg) self._impact = result_layer return result_layer
def run(self, layers): """Flood impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard_layer: Hazard raster layer of flood * exposure_layer: Vector layer of structure data on the same grid as hazard_layer """ # Extract data hazard_layer = get_hazard_layer(layers) # Depth exposure_layer = get_exposure_layer(layers) # Building locations question = get_question(hazard_layer.get_name(), exposure_layer.get_name(), self) # Determine attribute name for hazard levels hazard_attribute = None # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) buildings = {} # The number of affected buildings affected_count = 0 # The variable for regions mode affected_buildings = {} for i in range(total_features): # Use interpolated polygon attribute atts = features[i] # FIXME (Ole): Need to agree whether to use one or the # other as this can be very confusing! # For now look for 'affected' first if 'affected' in atts: # E.g. from flood forecast # Assume that building is wet if inside polygon # as flagged by attribute Flooded res = atts['affected'] if res is None: inundated_status = False else: inundated_status = bool(res) elif 'FLOODPRONE' in atts: res = atts['FLOODPRONE'] if res is None: inundated_status = False else: inundated_status = res.lower() == 'yes' elif DEFAULT_ATTRIBUTE in atts: # Check the default attribute assigned for points # covered by a polygon res = atts[DEFAULT_ATTRIBUTE] if res is None: inundated_status = False else: inundated_status = res else: # there is no flood related attribute message = ('No flood related attribute found in %s. I was ' 'looking for either "affected", "FLOODPRONE" or ' '"inapolygon". The latter should have been ' 'automatically set by call to ' 'assign_hazard_values_to_exposure_data(). Sorry I ' 'can\'t help more.') raise Exception(message) # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is not None and usage != 0: key = usage else: key = 'unknown' if key not in buildings: buildings[key] = 0 affected_buildings[key] = 0 # Count all buildings by type buildings[key] += 1 if inundated_status is True: # Count affected buildings by type affected_buildings[key] += 1 # Count total affected buildings affected_count += 1 # Add calculated impact to existing attributes features[i][self.target_field] = int(inundated_status) # Lump small entries and 'unknown' into 'other' category for usage in buildings.keys(): x = buildings[usage] if x < 25 or usage == 'unknown': if 'other' not in buildings: buildings['other'] = 0 affected_buildings['other'] = 0 buildings['other'] += x affected_buildings['other'] += affected_buildings[usage] del buildings[usage] del affected_buildings[usage] # Generate simple impact report table_body = [ question, TableRow([tr('Building type'), tr('Number flooded'), tr('Total')], header=True), TableRow([ tr('All'), format_int(affected_count), format_int(total_features) ]) ] school_closed = 0 hospital_closed = 0 # Generate break down by building usage type if available list_type_attribute = [ 'TYPE', 'type', 'amenity', 'building_t', 'office', 'tourism', 'leisure', 'building' ] intersect_type = set(attribute_names) & set(list_type_attribute) if len(intersect_type) > 0: # Make list of building types building_list = [] for usage in buildings: building_type = usage.replace('_', ' ') # Lookup internationalised value if available building_type = tr(building_type) building_list.append([ building_type.capitalize(), format_int(affected_buildings[usage]), format_int(buildings[usage]) ]) if usage.lower() == 'school': school_closed = affected_buildings[usage] if usage.lower() == 'hospital': hospital_closed = affected_buildings[usage] # Sort alphabetically building_list.sort() table_body.append( TableRow(tr('Breakdown by building type'), header=True)) for row in building_list: s = TableRow(row) table_body.append(s) # Action Checklist Section table_body.append(TableRow(tr('Action Checklist:'), header=True)) table_body.append( TableRow(tr('Are the critical facilities still open?'))) table_body.append( TableRow( tr('Which structures have warning capacity (eg. sirens, speakers, ' 'etc.)?'))) table_body.append( TableRow(tr('Which buildings will be evacuation centres?'))) table_body.append( TableRow(tr('Where will we locate the operations centre?'))) table_body.append( TableRow( tr('Where will we locate warehouse and/or distribution centres?' ))) if school_closed > 0: table_body.append( TableRow( tr('Where will the students from the %s closed schools go to ' 'study?') % format_int(school_closed))) if hospital_closed > 0: table_body.append( TableRow( tr('Where will the patients from the %s closed hospitals go ' 'for treatment and how will we transport them?') % format_int(hospital_closed))) # Notes Section table_body.append(TableRow(tr('Notes'), header=True)) table_body.append( TableRow( tr('Buildings are said to be flooded when in regions marked ' 'as affected'))) # Result impact_summary = Table(table_body).toNewlineFreeString() impact_table = impact_summary # Prepare impact layer map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') style_classes = [ dict(label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=1), dict(label=tr('Inundated'), value=1, colour='#F31A1C', ztransparency=0, size=1) ] legend_units = tr('(inundated or not inundated)') style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Create vector layer and return vector_layer = Vector(data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': total_features, 'buildings_affected': affected_count }, style_info=style_info) return vector_layer
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
def run(self): """Flood impact to buildings (e.g. from Open Street Map).""" self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') threshold = self.parameters['threshold'].value # Flood threshold [m] verify(isinstance(threshold, float), 'Expected thresholds to be a float. Got %s' % str(threshold)) # Determine attribute name for hazard levels hazard_attribute = 'depth' # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([(tr('Flooded'), {}), (tr('Wet'), {}), (tr('Dry'), {})]) for i in range(total_features): # Get the interpolated depth water_depth = float(features[i]['depth']) if water_depth <= 0: inundated_status = 0 # dry elif water_depth >= threshold: inundated_status = 1 # inundated else: inundated_status = 2 # wet # Count affected buildings by usage type if available if (structure_class_field in attribute_names and structure_class_field): usage = features[i].get(structure_class_field, None) else: usage = get_osm_building_usage(attribute_names, features[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) # Count all buildings by type self.buildings[usage] += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status category = [tr('Dry'), tr('Flooded'), tr('Wet')][inundated_status] self.affected_buildings[category][usage][tr( 'Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Generate simple impact report impact_table = impact_summary = self.html_report() # For printing map purpose map_title = tr('Flooded buildings') legend_title = tr('Flooded structure status') legend_units = tr('(flooded, wet, or dry)') style_classes = [ dict(label=tr('Dry (<= 0 m)'), value=0, colour='#1EFC7C', transparency=0, size=1), dict(label=tr('Wet (0 m - %.1f m)') % threshold, value=2, colour='#FF9900', transparency=0, size=1), dict(label=tr('Flooded (>= %.1f m)') % threshold, value=1, colour='#F31A1C', transparency=0, size=1) ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_title': legend_title, 'legend_units': legend_units, 'buildings_total': total_features, 'buildings_affected': self.total_affected_buildings } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) vector_layer = Vector(data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords=impact_layer_keywords, style_info=style_info) # Create vector layer and return self._impact = vector_layer return vector_layer
def run(self, layers=None): """Counts number of building exposed to each volcano hazard zones. :param layers: List of layers expected to contain. * hazard_layer: Hazard layer of volcano * exposure_layer: Vector layer of structure data on the same grid as hazard_layer :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare(layers) # Target Field target_field = 'zone' # Hazard Zone Attribute hazard_zone_attribute = 'radius' # Not Affected Value not_affected_value = 'Not Affected' # Parameters radii = self.parameters['distances [km]'] volcano_name_attribute = self.parameters['volcano name attribute'] # Identify hazard and exposure layers hazard_layer = self.hazard # Volcano hazard layer exposure_layer = self.exposure # Building exposure layer # Input checks if not hazard_layer.is_point_data: message = ( 'Input hazard must be a vector point layer. I got %s ' 'with layer type %s' % (hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(message) # Make hazard layer by buffering the point centers = hazard_layer.get_geometry() features = hazard_layer.get_data() radii_meter = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points(centers, radii_meter, hazard_zone_attribute, data_table=features) # Category names for the impact zone category_names = radii_meter category_names.append(not_affected_value) # Get names of volcanoes considered if volcano_name_attribute in hazard_layer.get_attribute_names(): volcano_name_list = set() for row in hazard_layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[volcano_name_attribute]) self.volcano_names = ', '.join(volcano_name_list) else: self.volcano_names = tr('Not specified in data') # Find the target field name that has no conflict with the attribute # names in the hazard layer hazard_attribute_names = hazard_layer.get_attribute_names() target_field = get_non_conflicting_attribute_name( target_field, hazard_attribute_names) # Run interpolation function for polygon2polygon interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=None) # Extract relevant interpolated layer data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} self.affected_buildings = OrderedDict() for category in radii_meter: self.affected_buildings[category] = {} # Iterate the interpolated building layer for i in range(len(features)): hazard_value = features[i][hazard_zone_attribute] if not hazard_value: hazard_value = not_affected_value features[i][target_field] = hazard_value # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][tr( 'Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.generate_html_report() # Create style colours = [ '#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000' ] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic buffered point') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) legend_units = tr('(building)') legend_title = tr('Building count') # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic buffered point'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title }, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self): """Classified hazard impact to buildings (e.g. from Open Street Map). """ self.validate() self.prepare() self.provenance.append_step( 'Calculating Step', 'Impact function is calculating the impact.') # Value from layer's keywords # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None # The 3 classes categorical_hazards = self.parameters['Categorical hazards'].value low_t = categorical_hazards[0].value medium_t = categorical_hazards[1].value high_t = categorical_hazards[2].value # Determine attribute name for hazard levels if self.hazard.layer.is_raster: hazard_attribute = 'level' else: hazard_attribute = None interpolated_result = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute, mode='constant') # Extract relevant exposure data attribute_names = interpolated_result.get_attribute_names() attributes = interpolated_result.get_data() buildings_total = len(interpolated_result) # Calculate building impact self.buildings = {} self.affected_buildings = OrderedDict([(tr('High Hazard Class'), {}), (tr('Medium Hazard Class'), {}), (tr('Low Hazard Class'), {})]) for i in range(buildings_total): if (structure_class_field and structure_class_field in attribute_names): usage = attributes[i][structure_class_field] else: usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0) ]) # Count all buildings by type self.buildings[usage] += 1 attributes[i][self.target_field] = 0 attributes[i][self.affected_field] = 0 level = float(attributes[i]['level']) level = float(numpy_round(level)) if level == high_t: impact_level = tr('High Hazard Class') elif level == medium_t: impact_level = tr('Medium Hazard Class') elif level == low_t: impact_level = tr('Low Hazard Class') else: continue # Add calculated impact to existing attributes attributes[i][self.target_field] = { tr('High Hazard Class'): 3, tr('Medium Hazard Class'): 2, tr('Low Hazard Class'): 1 }[impact_level] attributes[i][self.affected_field] = 1 # Count affected buildings by type self.affected_buildings[impact_level][usage][tr( 'Buildings Affected')] += 1 # Consolidate the small building usage groups < 25 to other # Building threshold #2468 postprocessors = self.parameters['postprocessors'] building_postprocessors = postprocessors['BuildingType'][0] self.building_report_threshold = building_postprocessors.value[0].value self._consolidate_to_other() # Create style style_classes = [ dict(label=tr('High'), value=3, colour='#F31A1C', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Medium'), value=2, colour='#F4A442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Low'), value=1, colour='#EBF442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Not Affected'), value=None, colour='#1EFC7C', transparency=0, size=2, border_color='#969696', border_width=0.2) ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_table = impact_summary = self.html_report() # For printing map purpose map_title = tr('Buildings affected') legend_title = tr('Structure inundated status') legend_units = tr('(Low, Medium, High)') extra_keywords = { 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.affected_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': buildings_total, 'buildings_affected': self.total_affected_buildings } self.set_if_provenance() impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return vector_layer = Vector(data=attributes, projection=self.exposure.layer.get_projection(), geometry=self.exposure.layer.get_geometry(), name=tr('Estimated buildings affected'), keywords=impact_layer_keywords, style_info=style_info) self._impact = vector_layer return vector_layer
def run(self): """Risk plugin for volcano hazard on building/structure. Counts number of building exposed to each volcano hazard zones. :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() # Get parameters from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') name_attribute = self.hazard.keyword('volcano_name_field') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None # Input checks if not self.hazard.layer.is_polygon_data: message = ( 'Input hazard must be a polygon. I got %s with ' 'layer type %s' % (self.hazard.name, self.hazard.layer.get_geometry_name())) raise Exception(message) # Check if hazard_zone_attribute exists in hazard_layer if (self.hazard_class_attribute not in self.hazard.layer.get_attribute_names()): message = ( 'Hazard data %s did not contain expected attribute %s ' % (self.hazard.name, self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Get names of volcanoes considered if name_attribute in self.hazard.layer.get_attribute_names(): volcano_name_list = set() for row in self.hazard.layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[name_attribute]) self.volcano_names = ', '.join(volcano_name_list) else: self.volcano_names = tr('Not specified in data') # Run interpolation function for polygon2raster interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() # Hazard zone categories from hazard layer hazard_zone_categories = list( set(self.hazard.layer.get_data(self.hazard_class_attribute))) self.buildings = {} self.affected_buildings = OrderedDict() for hazard_category in hazard_zone_categories: self.affected_buildings[hazard_category] = {} for i in range(len(features)): hazard_value = features[i][self.hazard_class_attribute] if not hazard_value: hazard_value = self._not_affected_value features[i][self.target_field] = hazard_value if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = features[i][self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, features[i]) if usage in [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.generate_html_report() category_names = hazard_zone_categories category_names.append(self._not_affected_value) # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic hazard zone'), keywords={'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers): """Flood impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard_layer: Hazard layer of flood * exposure_layer: Vector layer of structure data on the same grid as hazard_layer """ threshold = self.parameters['threshold [m]'] # Flood threshold [m] verify(isinstance(threshold, float), 'Expected thresholds to be a float. Got %s' % str(threshold)) # Extract data hazard_layer = get_hazard_layer(layers) # Depth exposure_layer = get_exposure_layer(layers) # Building locations question = get_question( hazard_layer.get_name(), exposure_layer.get_name(), self) # Determine attribute name for hazard levels if hazard_layer.is_raster: mode = 'grid' hazard_attribute = 'depth' else: mode = 'regions' hazard_attribute = None # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) buildings = {} # The number of affected buildings affected_count = 0 # The variable for grid mode inundated_count = 0 wet_count = 0 dry_count = 0 inundated_buildings = {} wet_buildings = {} dry_buildings = {} # The variable for regions mode affected_buildings = {} if mode == 'grid': for i in range(total_features): # Get the interpolated depth water_depth = float(features[i]['depth']) if water_depth <= 0: inundated_status = 0 # dry elif water_depth >= threshold: inundated_status = 1 # inundated else: inundated_status = 2 # wet # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is not None and usage != 0: key = usage else: key = 'unknown' if key not in buildings: buildings[key] = 0 inundated_buildings[key] = 0 wet_buildings[key] = 0 dry_buildings[key] = 0 # Count all buildings by type buildings[key] += 1 if inundated_status is 0: # Count dry buildings by type dry_buildings[key] += 1 # Count total dry buildings dry_count += 1 if inundated_status is 1: # Count inundated buildings by type inundated_buildings[key] += 1 # Count total dry buildings inundated_count += 1 if inundated_status is 2: # Count wet buildings by type wet_buildings[key] += 1 # Count total wet buildings wet_count += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status elif mode == 'regions': for i in range(total_features): # Use interpolated polygon attribute atts = features[i] # FIXME (Ole): Need to agree whether to use one or the # other as this can be very confusing! # For now look for 'affected' first if 'affected' in atts: # E.g. from flood forecast # Assume that building is wet if inside polygon # as flagged by attribute Flooded res = atts['affected'] if res is None: inundated_status = False else: inundated_status = bool(res) elif 'FLOODPRONE' in atts: res = atts['FLOODPRONE'] if res is None: inundated_status = False else: inundated_status = res.lower() == 'yes' elif DEFAULT_ATTRIBUTE in atts: # Check the default attribute assigned for points # covered by a polygon res = atts[DEFAULT_ATTRIBUTE] if res is None: inundated_status = False else: inundated_status = res else: # there is no flood related attribute message = ( 'No flood related attribute found in %s. I was ' 'looking for either "affected", "FLOODPRONE" or ' '"inapolygon". The latter should have been ' 'automatically set by call to ' 'assign_hazard_values_to_exposure_data(). Sorry I ' 'can\'t help more.') raise Exception(message) # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is not None and usage != 0: key = usage else: key = 'unknown' if key not in buildings: buildings[key] = 0 affected_buildings[key] = 0 # Count all buildings by type buildings[key] += 1 if inundated_status is True: # Count affected buildings by type affected_buildings[key] += 1 # Count total affected buildings affected_count += 1 # Add calculated impact to existing attributes features[i][self.target_field] = int(inundated_status) else: message = (tr('Unknown hazard type %s. Must be either "depth" or ' '"grid"') % mode) raise Exception(message) if mode == 'grid': affected_count = inundated_count + wet_count # Lump small entries and 'unknown' into 'other' category for usage in buildings.keys(): x = buildings[usage] if x < 25 or usage == 'unknown': if 'other' not in buildings: buildings['other'] = 0 if mode == 'grid': inundated_buildings['other'] = 0 wet_buildings['other'] = 0 dry_buildings['other'] = 0 elif mode == 'regions': affected_buildings['other'] = 0 buildings['other'] += x if mode == 'grid': inundated_buildings['other'] += inundated_buildings[usage] wet_buildings['other'] += wet_buildings[usage] dry_buildings['other'] += dry_buildings[usage] del buildings[usage] del inundated_buildings[usage] del wet_buildings[usage] del dry_buildings[usage] elif mode == 'regions': affected_buildings['other'] += affected_buildings[usage] del buildings[usage] del affected_buildings[usage] # Generate simple impact report table_body = [] if mode == 'grid': table_body = [ question, TableRow([tr('Building type'), tr('Number Inundated'), tr('Number of Wet Buildings'), tr('Number of Dry Buildings'), tr('Total')], header=True), TableRow( [tr('All'), format_int(inundated_count), format_int(wet_count), format_int(dry_count), format_int(total_features)])] elif mode == 'regions': table_body = [ question, TableRow([tr('Building type'), tr('Number flooded'), tr('Total')], header=True), TableRow([tr('All'), format_int(affected_count), format_int(total_features)])] school_closed = 0 hospital_closed = 0 # Generate break down by building usage type if available list_type_attribute = [ 'TYPE', 'type', 'amenity', 'building_t', 'office', 'tourism', 'leisure', 'building'] intersect_type = set(attribute_names) & set(list_type_attribute) if len(intersect_type) > 0: # Make list of building types building_list = [] for usage in buildings: building_type = usage.replace('_', ' ') # Lookup internationalised value if available building_type = tr(building_type) if mode == 'grid': building_list.append([ building_type.capitalize(), format_int(inundated_buildings[usage]), format_int(wet_buildings[usage]), format_int(dry_buildings[usage]), format_int(buildings[usage])]) elif mode == 'regions': building_list.append([ building_type.capitalize(), format_int(affected_buildings[usage]), format_int(buildings[usage])]) if usage.lower() == 'school': school_closed = 0 if mode == 'grid': school_closed += inundated_buildings[usage] school_closed += wet_buildings[usage] elif mode == 'regions': school_closed = affected_buildings[usage] if usage.lower() == 'hospital': hospital_closed = 0 if mode == 'grid': hospital_closed += inundated_buildings[usage] hospital_closed += wet_buildings[usage] elif mode == 'regions': hospital_closed = affected_buildings[usage] # Sort alphabetically building_list.sort() table_body.append(TableRow(tr('Breakdown by building type'), header=True)) for row in building_list: s = TableRow(row) table_body.append(s) # Action Checklist Section table_body.append(TableRow(tr('Action Checklist:'), header=True)) table_body.append(TableRow( tr('Are the critical facilities still open?'))) table_body.append(TableRow( tr('Which structures have warning capacity (eg. sirens, speakers, ' 'etc.)?'))) table_body.append(TableRow( tr('Which buildings will be evacuation centres?'))) table_body.append(TableRow( tr('Where will we locate the operations centre?'))) table_body.append(TableRow( tr('Where will we locate warehouse and/or distribution centres?'))) if school_closed > 0: table_body.append(TableRow( tr('Where will the students from the %s closed schools go to ' 'study?') % format_int(school_closed))) if hospital_closed > 0: table_body.append(TableRow( tr('Where will the patients from the %s closed hospitals go ' 'for treatment and how will we transport them?') % format_int(hospital_closed))) # Notes Section table_body.append(TableRow(tr('Notes'), header=True)) if mode == 'grid': table_body.append(TableRow( tr('Buildings are said to be inundated when flood levels ' 'exceed %.1f m') % threshold)) table_body.append(TableRow( tr('Buildings are said to be wet when flood levels ' 'are greater than 0 m but less than %.1f m') % threshold)) table_body.append(TableRow( tr('Buildings are said to be dry when flood levels ' 'are less than 0 m'))) table_body.append(TableRow( tr('Buildings are said to be closed if they are inundated or ' 'wet'))) table_body.append(TableRow( tr('Buildings are said to be open if they are dry'))) else: table_body.append(TableRow( tr('Buildings are said to be flooded when in regions marked ' 'as affected'))) # Result impact_summary = Table(table_body).toNewlineFreeString() impact_table = impact_summary # Prepare impact layer map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') legend_units = '' style_classes = [] if mode == 'grid': style_classes = [ dict( label=tr('Dry (<= 0 m)'), value=0, colour='#1EFC7C', transparency=0, size=1 ), dict( label=tr('Wet (0 m - %.1f m)') % threshold, value=2, colour='#FF9900', transparency=0, size=1 ), dict( label=tr('Inundated (>= %.1f m)') % threshold, value=1, colour='#F31A1C', transparency=0, size=1 )] legend_units = tr('(inundated, wet, or dry)') elif mode == 'regions': style_classes = [ dict( label=tr('Not Inundated'), value=0, colour='#1EFC7C', transparency=0, size=1), dict( label=tr('Inundated'), value=1, colour='#F31A1C', ztransparency=0, size=1)] legend_units = tr('(inundated or not inundated)') style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # Create vector layer and return vector_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': total_features, 'buildings_affected': affected_count}, style_info=style_info) return vector_layer
def run(self): """Risk plugin for classified polygon hazard on building/structure. Counts number of building exposed to each hazard zones. :returns: Map of building exposed to each hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare() # Value from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: self.exposure_class_attribute = None hazard_zone_attribute_index = self.hazard.layer.fieldNameIndex( self.hazard_class_attribute) # Check if hazard_zone_attribute exists in hazard_layer if hazard_zone_attribute_index < 0: message = ( 'Hazard data %s does not contain expected attribute %s ' % (self.hazard.layer.name(), self.hazard_class_attribute)) # noinspection PyExceptionInherit raise InaSAFEError(message) # Hazard zone categories from hazard layer self.hazard_zones = self.hazard.layer.uniqueValues( hazard_zone_attribute_index) self.buildings = {} self.affected_buildings = OrderedDict() for hazard_zone in self.hazard_zones: self.affected_buildings[hazard_zone] = {} wgs84_extent = QgsRectangle( self.requested_extent[0], self.requested_extent[1], self.requested_extent[2], self.requested_extent[3]) # Run interpolation function for polygon2polygon interpolated_layer = interpolate_polygon_polygon( self.hazard.layer, self.exposure.layer, wgs84_extent) new_field = QgsField(self.target_field, QVariant.String) interpolated_layer.dataProvider().addAttributes([new_field]) interpolated_layer.updateFields() attribute_names = [ field.name() for field in interpolated_layer.pendingFields()] target_field_index = interpolated_layer.fieldNameIndex( self.target_field) changed_values = {} if interpolated_layer.featureCount() < 1: raise ZeroImpactException() # Extract relevant interpolated data for feature in interpolated_layer.getFeatures(): hazard_value = feature[self.hazard_class_attribute] if not hazard_value: hazard_value = self._not_affected_value changed_values[feature.id()] = {target_field_index: hazard_value} if (self.exposure_class_attribute and self.exposure_class_attribute in attribute_names): usage = feature[self.exposure_class_attribute] else: usage = get_osm_building_usage(attribute_names, feature) if usage is None: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict( [(tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 interpolated_layer.dataProvider().changeAttributeValues(changed_values) # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.html_report() # Create style categories = self.hazard_zones categories.append(self._not_affected_value) colours = color_ramp(len(categories)) style_classes = [] i = 0 for hazard_zone in self.hazard_zones: style_class = dict() style_class['label'] = tr(hazard_zone) style_class['transparency'] = 0 style_class['value'] = hazard_zone style_class['size'] = 1 style_class['colour'] = colours[i] style_classes.append(style_class) i += 1 # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by each hazard zone') legend_title = tr('Building count') legend_units = tr('(building)') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) # Create vector layer and return impact_layer = Vector( data=interpolated_layer, name=tr('Buildings affected by each hazard zone'), keywords={'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Earthquake impact to buildings (e.g. from OpenStreetMap). :param layers: All the input layers (Hazard Layer and Exposure Layer) """ self.validate() self.prepare(layers) LOGGER.debug("Running earthquake building impact") # merely initialize building_value = 0 contents_value = 0 # Thresholds for mmi breakdown. t0 = self.parameters["low_threshold"] t1 = self.parameters["medium_threshold"] t2 = self.parameters["high_threshold"] # Class Attribute and Label. class_1 = {"label": tr("Low"), "class": 1} class_2 = {"label": tr("Medium"), "class": 2} class_3 = {"label": tr("High"), "class": 3} # Extract data hazard_layer = self.hazard # Depth exposure_layer = self.exposure # Building locations # Define attribute name for hazard levels. hazard_attribute = "mmi" # Determine if exposure data have NEXIS attributes. attribute_names = exposure_layer.get_attribute_names() if "FLOOR_AREA" in attribute_names and "BUILDING_C" in attribute_names and "CONTENTS_C" in attribute_names: self.is_nexis = True else: self.is_nexis = False # Interpolate hazard level to building locations. interpolate_result = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=hazard_attribute ) # Extract relevant exposure data # attribute_names = interpolate_result.get_attribute_names() attributes = interpolate_result.get_data() interpolate_size = len(interpolate_result) # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([(tr("High"), {}), (tr("Medium"), {}), (tr("Low"), {})]) for i in range(interpolate_size): # Classify building according to shake level # and calculate dollar losses if self.is_nexis: try: area = float(attributes[i]["FLOOR_AREA"]) except (ValueError, KeyError): # print 'Got area', attributes[i]['FLOOR_AREA'] area = 0.0 try: building_value_density = float(attributes[i]["BUILDING_C"]) except (ValueError, KeyError): # print 'Got bld value', attributes[i]['BUILDING_C'] building_value_density = 0.0 try: contents_value_density = float(attributes[i]["CONTENTS_C"]) except (ValueError, KeyError): # print 'Got cont value', attributes[i]['CONTENTS_C'] contents_value_density = 0.0 building_value = building_value_density * area contents_value = contents_value_density * area usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = "unknown" if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): if self.is_nexis: self.affected_buildings[category][usage] = OrderedDict( [ (tr("Buildings Affected"), 0), (tr("Buildings value ($M)"), 0), (tr("Contents value ($M)"), 0), ] ) else: self.affected_buildings[category][usage] = OrderedDict([(tr("Buildings Affected"), 0)]) self.buildings[usage] += 1 try: mmi = float(attributes[i][hazard_attribute]) # MMI except TypeError: mmi = 0.0 if t0 <= mmi < t1: cls = 1 category = tr("Low") elif t1 <= mmi < t2: cls = 2 category = tr("Medium") elif t2 <= mmi: cls = 3 category = tr("High") else: # Not reported for less than level t0 continue attributes[i][self.target_field] = cls self.affected_buildings[category][usage][tr("Buildings Affected")] += 1 if self.is_nexis: self.affected_buildings[category][usage][tr("Buildings value ($M)")] += building_value / 1000000.0 self.affected_buildings[category][usage][tr("Contents value ($M)")] += contents_value / 1000000.0 # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() impact_table = impact_summary = self.generate_html_report() # Create style style_classes = [ dict(label=class_1["label"], value=class_1["class"], colour="#ffff00", transparency=1), dict(label=class_2["label"], value=class_2["class"], colour="#ffaa00", transparency=1), dict(label=class_3["label"], value=class_3["class"], colour="#ff0000", transparency=1), ] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type="categorizedSymbol") # For printing map purpose map_title = tr("Building affected by earthquake") legend_notes = tr("The level of the impact is according to the " "threshold the user input.") legend_units = tr("(mmi)") legend_title = tr("Impact level") # Create vector layer and return result_layer = Vector( data=attributes, projection=interpolate_result.get_projection(), geometry=interpolate_result.get_geometry(), name=tr("Estimated buildings affected"), keywords={ "impact_summary": impact_summary, "impact_table": impact_table, "map_title": map_title, "legend_notes": legend_notes, "legend_units": legend_units, "legend_title": legend_title, "target_field": self.target_field, "statistics_type": self.statistics_type, "statistics_classes": self.statistics_classes, }, style_info=style_info, ) msg = "Created vector layer %s" % str(result_layer) LOGGER.debug(msg) self._impact = result_layer return result_layer
def run(self): """Flood impact to buildings (e.g. from Open Street Map).""" self.validate() self.prepare() threshold = self.parameters['threshold'].value # Flood threshold [m] verify(isinstance(threshold, float), 'Expected thresholds to be a float. Got %s' % str(threshold)) # Determine attribute name for hazard levels hazard_attribute = 'depth' # Interpolate hazard level to building locations interpolated_layer = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute) # Extract relevant exposure data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() total_features = len(interpolated_layer) # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (tr('Number Inundated'), {}), (tr('Number of Wet Buildings'), {}), (tr('Number of Dry Buildings'), {}) ]) for i in range(total_features): # Get the interpolated depth water_depth = float(features[i]['depth']) if water_depth <= 0: inundated_status = 0 # dry elif water_depth >= threshold: inundated_status = 1 # inundated else: inundated_status = 2 # wet # Count affected buildings by usage type if available if (structure_class_field in attribute_names and structure_class_field): usage = features[i].get(structure_class_field, None) else: usage = get_osm_building_usage( attribute_names, features[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) # Count all buildings by type self.buildings[usage] += 1 # Add calculated impact to existing attributes features[i][self.target_field] = inundated_status category = [ tr('Number of Dry Buildings'), tr('Number Inundated'), tr('Number of Wet Buildings')][inundated_status] self.affected_buildings[category][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_table = impact_summary = self.generate_html_report() # For printing map purpose map_title = tr('Buildings inundated') legend_title = tr('Structure inundated status') legend_units = tr('(inundated, wet, or dry)') style_classes = [ dict( label=tr('Dry (<= 0 m)'), value=0, colour='#1EFC7C', transparency=0, size=1 ), dict( label=tr('Wet (0 m - %.1f m)') % threshold, value=2, colour='#FF9900', transparency=0, size=1 ), dict( label=tr('Inundated (>= %.1f m)') % threshold, value=1, colour='#F31A1C', transparency=0, size=1 )] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') vector_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.target_field, 'map_title': map_title, 'legend_title': legend_title, 'legend_units': legend_units, 'buildings_total': total_features, 'buildings_affected': self.total_affected_buildings}, style_info=style_info) # Create vector layer and return self._impact = vector_layer return vector_layer
def run(self): """Earthquake impact to buildings (e.g. from OpenStreetMap).""" self.validate() self.prepare() LOGGER.debug('Running earthquake building impact') # merely initialize building_value = 0 contents_value = 0 # Thresholds for mmi breakdown. t0 = self.parameters['low_threshold'].value t1 = self.parameters['medium_threshold'].value t2 = self.parameters['high_threshold'].value # Class Attribute and Label. class_1 = {'label': tr('Low'), 'class': 1} class_2 = {'label': tr('Medium'), 'class': 2} class_3 = {'label': tr('High'), 'class': 3} # Define attribute name for hazard levels. hazard_attribute = 'mmi' # Determine if exposure data have NEXIS attributes. attribute_names = self.exposure.layer.get_attribute_names() if ( 'FLOOR_AREA' in attribute_names and 'BUILDING_C' in attribute_names and 'CONTENTS_C' in attribute_names): self.is_nexis = True else: self.is_nexis = False # Interpolate hazard level to building locations. interpolate_result = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute ) # Extract relevant exposure data # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None attributes = interpolate_result.get_data() interpolate_size = len(interpolate_result) # Building breakdown self.buildings = {} # Impacted building breakdown self.affected_buildings = OrderedDict([ (tr('High'), {}), (tr('Medium'), {}), (tr('Low'), {}) ]) removed = [] for i in range(interpolate_size): # Classify building according to shake level # and calculate dollar losses if self.is_nexis: try: area = float(attributes[i]['FLOOR_AREA']) except (ValueError, KeyError): # print 'Got area', attributes[i]['FLOOR_AREA'] area = 0.0 try: building_value_density = float(attributes[i]['BUILDING_C']) except (ValueError, KeyError): # print 'Got bld value', attributes[i]['BUILDING_C'] building_value_density = 0.0 try: contents_value_density = float(attributes[i]['CONTENTS_C']) except (ValueError, KeyError): # print 'Got cont value', attributes[i]['CONTENTS_C'] contents_value_density = 0.0 building_value = building_value_density * area contents_value = contents_value_density * area if (structure_class_field in attribute_names and structure_class_field): usage = attributes[i].get(structure_class_field, None) else: usage = get_osm_building_usage( attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): if self.is_nexis: self.affected_buildings[category][usage] = OrderedDict( [ (tr('Buildings Affected'), 0), (tr('Buildings value ($M)'), 0), (tr('Contents value ($M)'), 0)]) else: self.affected_buildings[category][usage] = \ OrderedDict([(tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 try: mmi = float(attributes[i][hazard_attribute]) # MMI except TypeError: mmi = 0.0 if t0 <= mmi < t1: cls = 1 category = tr('Low') elif t1 <= mmi < t2: cls = 2 category = tr('Medium') elif t2 <= mmi: cls = 3 category = tr('High') else: # Not reported for less than level t0 removed.append(i) continue attributes[i][self.target_field] = cls self.affected_buildings[ category][usage][tr('Buildings Affected')] += 1 if self.is_nexis: self.affected_buildings[category][usage][ tr('Buildings value ($M)')] += building_value / 1000000.0 self.affected_buildings[category][usage][ tr('Contents value ($M)')] += contents_value / 1000000.0 # remove uncategorized element removed.reverse() geometry = interpolate_result.get_geometry() for i in range(0, len(removed)): del attributes[removed[i]] del geometry[removed[i]] if len(attributes) < 1: raise ZeroImpactException() # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() impact_table = impact_summary = self.html_report() # Create style style_classes = [dict(label=class_1['label'], value=class_1['class'], colour='#ffff00', transparency=1), dict(label=class_2['label'], value=class_2['class'], colour='#ffaa00', transparency=1), dict(label=class_3['label'], value=class_3['class'], colour='#ff0000', transparency=1)] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Building affected by earthquake') legend_notes = tr('The level of the impact is according to the ' 'threshold the user input.') legend_units = tr('(mmi)') legend_title = tr('Impact level') # Create vector layer and return result_layer = Vector( data=attributes, projection=interpolate_result.get_projection(), geometry=geometry, name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title, 'target_field': self.target_field, 'statistics_type': self.statistics_type, 'statistics_classes': self.statistics_classes}, style_info=style_info) msg = 'Created vector layer %s' % str(result_layer) LOGGER.debug(msg) self._impact = result_layer return result_layer
def run(self): """Classified hazard impact to buildings (e.g. from Open Street Map). """ self.validate() self.prepare() # Value from layer's keywords # Try to get the value from keyword, if not exist, it will not fail, # but use the old get_osm_building_usage try: structure_class_field = self.exposure.keyword( 'structure_class_field') except KeywordNotFoundError: structure_class_field = None # The 3 classes categorical_hazards = self.parameters['Categorical hazards'].value low_t = categorical_hazards[0].value medium_t = categorical_hazards[1].value high_t = categorical_hazards[2].value # Determine attribute name for hazard levels if self.hazard.layer.is_raster: hazard_attribute = 'level' else: hazard_attribute = None interpolated_result = assign_hazard_values_to_exposure_data( self.hazard.layer, self.exposure.layer, attribute_name=hazard_attribute, mode='constant') # Extract relevant exposure data attribute_names = interpolated_result.get_attribute_names() attributes = interpolated_result.get_data() buildings_total = len(interpolated_result) # Calculate building impact self.buildings = {} self.affected_buildings = OrderedDict([ (tr('High Hazard Class'), {}), (tr('Medium Hazard Class'), {}), (tr('Low Hazard Class'), {}) ]) for i in range(buildings_total): if (structure_class_field and structure_class_field in attribute_names): usage = attributes[i][structure_class_field] else: usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) # Count all buildings by type self.buildings[usage] += 1 attributes[i][self.target_field] = 0 attributes[i][self.affected_field] = 0 level = float(attributes[i]['level']) level = float(numpy_round(level)) if level == high_t: impact_level = tr('High Hazard Class') elif level == medium_t: impact_level = tr('Medium Hazard Class') elif level == low_t: impact_level = tr('Low Hazard Class') else: continue # Add calculated impact to existing attributes attributes[i][self.target_field] = { tr('High Hazard Class'): 3, tr('Medium Hazard Class'): 2, tr('Low Hazard Class'): 1 }[impact_level] attributes[i][self.affected_field] = 1 # Count affected buildings by type self.affected_buildings[impact_level][usage][ tr('Buildings Affected')] += 1 # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() # Create style style_classes = [dict(label=tr('High'), value=3, colour='#F31A1C', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Medium'), value=2, colour='#F4A442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Low'), value=1, colour='#EBF442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Not Affected'), value=None, colour='#1EFC7C', transparency=0, size=2, border_color='#969696', border_width=0.2)] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_table = impact_summary = self.html_report() # For printing map purpose map_title = tr('Buildings affected') legend_title = tr('Structure inundated status') legend_units = tr('(Low, Medium, High)') # Create vector layer and return vector_layer = Vector( data=attributes, projection=self.exposure.layer.get_projection(), geometry=self.exposure.layer.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.affected_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': buildings_total, 'buildings_affected': self.total_affected_buildings}, style_info=style_info) self._impact = vector_layer return vector_layer
def run(self, layers=None): """Counts number of building exposed to each volcano hazard zones. :param layers: List of layers expected to contain. * hazard_layer: Hazard layer of volcano * exposure_layer: Vector layer of structure data on the same grid as hazard_layer :returns: Map of building exposed to volcanic hazard zones. Table with number of buildings affected :rtype: dict """ self.validate() self.prepare(layers) # Target Field target_field = 'zone' # Hazard Zone Attribute hazard_zone_attribute = 'radius' # Not Affected Value not_affected_value = 'Not Affected' # Parameters radii = self.parameters['distances [km]'] volcano_name_attribute = self.parameters['volcano name attribute'] # Identify hazard and exposure layers hazard_layer = self.hazard # Volcano hazard layer exposure_layer = self.exposure # Building exposure layer # Input checks if not hazard_layer.is_point_data: message = ( 'Input hazard must be a vector point layer. I got %s ' 'with layer type %s' % ( hazard_layer.get_name(), hazard_layer.get_geometry_name())) raise Exception(message) # Make hazard layer by buffering the point centers = hazard_layer.get_geometry() features = hazard_layer.get_data() radii_meter = [x * 1000 for x in radii] # Convert to meters hazard_layer = buffer_points( centers, radii_meter, hazard_zone_attribute, data_table=features) # Category names for the impact zone category_names = radii_meter category_names.append(not_affected_value) # Get names of volcanoes considered if volcano_name_attribute in hazard_layer.get_attribute_names(): volcano_name_list = set() for row in hazard_layer.get_data(): # Run through all polygons and get unique names volcano_name_list.add(row[volcano_name_attribute]) self.volcano_names = ', '.join(volcano_name_list) # Find the target field name that has no conflict with the attribute # names in the hazard layer hazard_attribute_names = hazard_layer.get_attribute_names() target_field = get_non_conflicting_attribute_name( target_field, hazard_attribute_names) # Run interpolation function for polygon2polygon interpolated_layer = assign_hazard_values_to_exposure_data( hazard_layer, exposure_layer, attribute_name=None) # Extract relevant interpolated layer data attribute_names = interpolated_layer.get_attribute_names() features = interpolated_layer.get_data() self.buildings = {} self.affected_buildings = OrderedDict() for category in radii_meter: self.affected_buildings[category] = {} # Iterate the interpolated building layer for i in range(len(features)): hazard_value = features[i][hazard_zone_attribute] if not hazard_value: hazard_value = not_affected_value features[i][target_field] = hazard_value # Count affected buildings by usage type if available usage = get_osm_building_usage(attribute_names, features[i]) if usage is [None, 'NULL', 'null', 'Null', 0]: usage = tr('Unknown') if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][ usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) self.buildings[usage] += 1 if hazard_value in self.affected_buildings.keys(): self.affected_buildings[hazard_value][usage][ tr('Buildings Affected')] += 1 # Lump small entries and 'unknown' into 'other' category self._consolidate_to_other() # Generate simple impact report impact_summary = impact_table = self.generate_html_report() # Create style colours = ['#FFFFFF', '#38A800', '#79C900', '#CEED00', '#FFCC00', '#FF6600', '#FF0000', '#7A0000'] colours = colours[::-1] # flip colours = colours[:len(category_names)] style_classes = [] i = 0 for category_name in category_names: style_class = dict() style_class['label'] = tr(category_name) style_class['transparency'] = 0 style_class['value'] = category_name style_class['size'] = 1 if i >= len(category_names): i = len(category_names) - 1 style_class['colour'] = colours[i] i += 1 style_classes.append(style_class) # Override style info with new classes and name style_info = dict(target_field=target_field, style_classes=style_classes, style_type='categorizedSymbol') # For printing map purpose map_title = tr('Buildings affected by volcanic buffered point') legend_notes = tr('Thousand separator is represented by %s' % get_thousand_separator()) legend_units = tr('(building)') legend_title = tr('Building count') # Create vector layer and return impact_layer = Vector( data=features, projection=interpolated_layer.get_projection(), geometry=interpolated_layer.get_geometry(), name=tr('Buildings affected by volcanic buffered point'), keywords={'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': target_field, 'map_title': map_title, 'legend_notes': legend_notes, 'legend_units': legend_units, 'legend_title': legend_title}, style_info=style_info) self._impact = impact_layer return impact_layer
def run(self, layers=None): """Classified hazard impact to buildings (e.g. from Open Street Map). :param layers: List of layers expected to contain. * hazard: Classified Hazard layer * exposure: Vector layer of structure data on the same grid as hazard """ self.validate() self.prepare(layers) # The 3 classes low_t = self.parameters['low_hazard_class'] medium_t = self.parameters['medium_hazard_class'] high_t = self.parameters['high_hazard_class'] # Extract data hazard = self.hazard # Classified Hazard exposure = self.exposure # Building locations # Determine attribute name for hazard levels if hazard.is_raster: hazard_attribute = 'level' else: hazard_attribute = None interpolated_result = assign_hazard_values_to_exposure_data( hazard, exposure, attribute_name=hazard_attribute, mode='constant') # Extract relevant exposure data attribute_names = interpolated_result.get_attribute_names() attributes = interpolated_result.get_data() buildings_total = len(interpolated_result) # Calculate building impact self.buildings = {} self.affected_buildings = OrderedDict([ (tr('High Hazard Class'), {}), (tr('Medium Hazard Class'), {}), (tr('Low Hazard Class'), {}) ]) for i in range(buildings_total): usage = get_osm_building_usage(attribute_names, attributes[i]) if usage is None or usage == 0: usage = 'unknown' if usage not in self.buildings: self.buildings[usage] = 0 for category in self.affected_buildings.keys(): self.affected_buildings[category][usage] = OrderedDict([ (tr('Buildings Affected'), 0)]) # Count all buildings by type self.buildings[usage] += 1 attributes[i][self.target_field] = 0 attributes[i][self.affected_field] = 0 level = float(attributes[i]['level']) level = float(numpy_round(level)) if level == high_t: impact_level = tr('High Hazard Class') elif level == medium_t: impact_level = tr('Medium Hazard Class') elif level == low_t: impact_level = tr('Low Hazard Class') else: continue # Add calculated impact to existing attributes attributes[i][self.target_field] = { tr('High Hazard Class'): 3, tr('Medium Hazard Class'): 2, tr('Low Hazard Class'): 1 }[impact_level] attributes[i][self.affected_field] = 1 # Count affected buildings by type self.affected_buildings[impact_level][usage][ tr('Buildings Affected')] += 1 # Consolidate the small building usage groups < 25 to other self._consolidate_to_other() # Create style style_classes = [dict(label=tr('High'), value=3, colour='#F31A1C', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Medium'), value=2, colour='#F4A442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Low'), value=1, colour='#EBF442', transparency=0, size=2, border_color='#969696', border_width=0.2), dict(label=tr('Not Affected'), value=None, colour='#1EFC7C', transparency=0, size=2, border_color='#969696', border_width=0.2)] style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_table = impact_summary = self.generate_html_report() # For printing map purpose map_title = tr('Buildings affected') legend_units = tr('(Low, Medium, High)') legend_title = tr('Structure inundated status') # Create vector layer and return vector_layer = Vector( data=attributes, projection=exposure.get_projection(), geometry=exposure.get_geometry(), name=tr('Estimated buildings affected'), keywords={ 'impact_summary': impact_summary, 'impact_table': impact_table, 'target_field': self.affected_field, 'map_title': map_title, 'legend_units': legend_units, 'legend_title': legend_title, 'buildings_total': buildings_total, 'buildings_affected': self.total_affected_buildings}, style_info=style_info) self._impact = vector_layer return vector_layer