def test_color_ramp(self): """Test for color_ramp function.""" number_of_colours = 1 expected_colors = ['#ff0000'] colors = color_ramp(number_of_colours) self.assertEqual(colors, expected_colors) number_of_colours = 2 expected_colors = ['#ff0000', '#00ffff'] colors = color_ramp(number_of_colours) self.assertEqual(colors, expected_colors)
def test_color_ramp(self): """Test for color_ramp function.""" number_of_colours = 1 expected_colors = ['#ff0000'] colors = color_ramp(number_of_colours) message = 'Expecting %s, but it returns %s' % (expected_colors, colors) self.assertEqual(colors, expected_colors, message) number_of_colours = 2 expected_colors = ['#ff0000', '#00ffff'] colors = color_ramp(number_of_colours) message = 'Expecting %s, but it returns %s' % (expected_colors, colors) self.assertEqual(colors, expected_colors, message)
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): """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): """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 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 """ # Value from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') try: exposure_value_mapping = self.exposure.keyword('value_mapping') except KeywordNotFoundError: # Generic IF, the keyword might not be defined base.py exposure_value_mapping = {} # 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'] # Iterate over vector hazard classes 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 hazard_classes.append(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.init_report_var(hazard_classes) 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() 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} usage = feature[self.exposure_class_attribute] usage = main_type(usage, exposure_value_mapping) affected = False if hazard_value in self.hazard_class_mapping.keys(): affected = True self.classify_feature(hazard_value, usage, affected) interpolated_layer.dataProvider().changeAttributeValues(changed_values) self.reorder_dictionaries() # Create style categories = self.affected_buildings.keys() categories.append(self._not_affected_value) colours = color_ramp(len(categories)) style_classes = [] for i, hazard_zone in enumerate(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) # Override style info with new classes and name style_info = dict( target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol' ) impact_data = self.generate_data() extra_keywords = { 'target_field': self.target_field, 'map_title': self.map_title(), 'legend_notes': self.metadata().key('legend_notes'), 'legend_units': self.metadata().key('legend_units'), 'legend_title': self.metadata().key('legend_title') } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector( data=interpolated_layer, name=self.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def run(self): """Risk plugin for 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 """ # Value from layer's keywords self.hazard_class_attribute = self.hazard.keyword('field') self.hazard_class_mapping = self.hazard.keyword('value_map') self.exposure_class_attribute = self.exposure.keyword( 'structure_class_field') try: exposure_value_mapping = self.exposure.keyword('value_mapping') except KeywordNotFoundError: # Generic IF, the keyword might not be defined base.py exposure_value_mapping = {} # 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'] # Iterate over vector hazard classes 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 hazard_classes.append(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.init_report_var(hazard_classes) 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() 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} usage = feature[self.exposure_class_attribute] usage = main_type(usage, exposure_value_mapping) affected = False if hazard_value in self.hazard_class_mapping.keys(): affected = True self.classify_feature(hazard_value, usage, affected) interpolated_layer.dataProvider().changeAttributeValues(changed_values) self.reorder_dictionaries() # Create style categories = self.affected_buildings.keys() categories.append(self._not_affected_value) colours = color_ramp(len(categories)) style_classes = [] for i, hazard_zone in enumerate(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) # Override style info with new classes and name style_info = dict(target_field=self.target_field, style_classes=style_classes, style_type='categorizedSymbol') impact_data = self.generate_data() extra_keywords = { 'target_field': self.target_field, 'map_title': self.map_title(), 'legend_notes': self.metadata().key('legend_notes'), 'legend_units': self.metadata().key('legend_units'), 'legend_title': self.metadata().key('legend_title') } impact_layer_keywords = self.generate_impact_keywords(extra_keywords) # Create vector layer and return impact_layer = Vector(data=interpolated_layer, name=self.map_title(), keywords=impact_layer_keywords, style_info=style_info) impact_layer.impact_data = impact_data self._impact = impact_layer return impact_layer
def run(self): """Risk plugin for 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