def analysis_summary(aggregate_hazard, analysis, callback=None): """Compute the summary from the aggregate hazard to analysis. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | analysis_id | Output layer : | analysis_id | count_hazard_class | affected_count | total | :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param analysis: The target vector layer where to write statistics. :type analysis: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new target layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_3_analysis_steps['output_layer_name'] processing_step = summary_3_analysis_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = analysis.keywords['inasafe_fields'] target_compulsory_fields = [ analysis_id_field, analysis_name_field, ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, total_field ] check_inputs(source_compulsory_fields, source_fields) absolute_values = create_absolute_values_structure(aggregate_hazard, ['all']) hazard_class = source_fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] classification = hazard_keywords['classification'] total = source_fields[total_field['key']] flat_table = FlatTable('hazard_class') # First loop over the aggregate_hazard layer request = QgsFeatureRequest() request.setSubsetOfAttributes([hazard_class, total], aggregate_hazard.fields()) request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregate_hazard.getFeatures(): hazard_value = area[hazard_class_index] value = area[total] if not value or isinstance(value, QPyNullVariant) or isnan(value): # For isnan, see ticket #3812 value = 0 if not hazard_value or isinstance(hazard_value, QPyNullVariant): hazard_value = 'NULL' flat_table.add_value(value, hazard_class=hazard_value) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value(value, all='all') analysis.startEditing() shift = analysis.fields().count() counts = [ total_affected_field, total_not_affected_field, total_not_exposed_field, total_field ] add_fields(analysis, absolute_values, counts, unique_hazard, hazard_count_field) affected_sum = 0 not_affected_sum = 0 not_exposed_sum = 0 for area in analysis.getFeatures(request): total = 0 for i, val in enumerate(unique_hazard): if not val or isinstance(val, QPyNullVariant): val = 'NULL' sum = flat_table.get_value(hazard_class=val) total += sum analysis.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( classification=classification, hazard_class=val) if affected == not_exposed_class['key']: not_exposed_sum += sum elif affected: affected_sum += sum else: not_affected_sum += sum # Affected field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard), affected_sum) # Not affected field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 1, not_affected_sum) # Not exposed field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 2, not_exposed_sum) # Total field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 3, total) # Any absolute postprocessors for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value(all='all') analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 4 + i, value) # Sanity check ± 1 to the result. Disabled for now as it seems ± 1 is not # enough. ET 13/02/17 # total_computed = ( # affected_sum + not_affected_sum + not_exposed_sum) # if not -1 < (total_computed - total) < 1: # raise ComputationError analysis.commitChanges() analysis.keywords['title'] = layer_purpose_analysis_impacted['name'] if qgis_version() >= 21600: analysis.setName(analysis.keywords['title']) else: analysis.setLayerName(analysis.keywords['title']) analysis.keywords['layer_purpose'] = layer_purpose_analysis_impacted['key'] check_layer(analysis) return analysis
def analysis_summary(aggregate_hazard, analysis, callback=None): """Compute the summary from the aggregate hazard to analysis. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | analysis_id | Output layer : | analysis_id | count_hazard_class | affected_count | total | :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param analysis: The target vector layer where to write statistics. :type analysis: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new target layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_3_analysis_steps['output_layer_name'] processing_step = summary_3_analysis_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = analysis.keywords['inasafe_fields'] target_compulsory_fields = [ analysis_id_field, analysis_name_field, ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, total_field ] check_inputs(source_compulsory_fields, source_fields) absolute_values = create_absolute_values_structure( aggregate_hazard, ['all']) hazard_class = source_fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] classification = hazard_keywords['classification'] total = source_fields[total_field['key']] flat_table = FlatTable('hazard_class') # First loop over the aggregate_hazard layer request = QgsFeatureRequest() request.setSubsetOfAttributes( [hazard_class, total], aggregate_hazard.fields()) request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregate_hazard.getFeatures(): hazard_value = area[hazard_class_index] value = area[total] if not value or isinstance(value, QPyNullVariant) or isnan(value): # For isnan, see ticket #3812 value = 0 if not hazard_value or isinstance(hazard_value, QPyNullVariant): hazard_value = 'NULL' flat_table.add_value( value, hazard_class=hazard_value ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, all='all' ) analysis.startEditing() shift = analysis.fields().count() counts = [ total_affected_field, total_not_affected_field, total_not_exposed_field, total_field] add_fields( analysis, absolute_values, counts, unique_hazard, hazard_count_field) affected_sum = 0 not_affected_sum = 0 not_exposed_sum = 0 for area in analysis.getFeatures(request): total = 0 for i, val in enumerate(unique_hazard): if not val or isinstance(val, QPyNullVariant): val = 'NULL' sum = flat_table.get_value(hazard_class=val) total += sum analysis.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( classification=classification, hazard_class=val) if affected == not_exposed_class['key']: not_exposed_sum += sum elif affected: affected_sum += sum else: not_affected_sum += sum # Affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard), affected_sum) # Not affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 1, not_affected_sum) # Not exposed field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 2, not_exposed_sum) # Total field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 3, total) # Any absolute postprocessors for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( all='all' ) analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 4 + i, value) # Sanity check ± 1 to the result. Disabled for now as it seems ± 1 is not # enough. ET 13/02/17 # total_computed = ( # affected_sum + not_affected_sum + not_exposed_sum) # if not -1 < (total_computed - total) < 1: # raise ComputationError analysis.commitChanges() analysis.keywords['title'] = layer_purpose_analysis_impacted['name'] if qgis_version() >= 21600: analysis.setName(analysis.keywords['title']) else: analysis.setLayerName(analysis.keywords['title']) analysis.keywords['layer_purpose'] = layer_purpose_analysis_impacted['key'] check_layer(analysis) return analysis
def aggregate_hazard_summary(impact, aggregate_hazard, callback=None): """Compute the summary from the source layer to the aggregate_hazard layer. Source layer : |exp_id|exp_class|haz_id|haz_class|aggr_id|aggr_name|affected|extra*| Target layer : | aggr_id | aggr_name | haz_id | haz_class | extra* | Output layer : |aggr_id| aggr_name|haz_id|haz_class|affected|extra*|count ber exposure*| :param impact: The layer to aggregate vector layer. :type impact: QgsVectorLayer :param aggregate_hazard: The aggregate_hazard vector layer where to write statistics. :type aggregate_hazard: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new aggregate_hazard layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_1_aggregate_hazard_steps['output_layer_name'] processing_step = summary_1_aggregate_hazard_steps['step_name'] source_fields = impact.keywords['inasafe_fields'] target_fields = aggregate_hazard.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ exposure_id_field, exposure_class_field, aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(source_compulsory_fields, source_fields) aggregation_id = target_fields[aggregation_id_field['key']] hazard_id = target_fields[hazard_id_field['key']] hazard_class = target_fields[hazard_class_field['key']] exposure_class = source_fields[exposure_class_field['key']] exposure_class_index = impact.fieldNameIndex(exposure_class) unique_exposure = impact.uniqueValues(exposure_class_index) fields = ['aggregation_id', 'hazard_id'] absolute_values = create_absolute_values_structure(impact, fields) # We need to know what kind of exposure we are going to count. # the size, or the number of features or population. field_index = report_on_field(impact) aggregate_hazard.startEditing() shift = aggregate_hazard.fields().count() add_fields( aggregate_hazard, absolute_values, [affected_field, total_field], unique_exposure, exposure_count_field ) flat_table = FlatTable('aggregation_id', 'hazard_id', 'exposure_class') request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) LOGGER.debug('Computing the aggregate hazard summary.') for feature in impact.getFeatures(request): # Field_index can be equal to 0. if field_index is not None: value = feature[field_index] else: value = 1 aggregation_value = feature[aggregation_id] hazard_value = feature[hazard_id] if not hazard_value or isinstance(hazard_value, QPyNullVariant): hazard_value = not_exposed_class['key'] exposure_value = feature[exposure_class] if not exposure_value or isinstance(exposure_value, QPyNullVariant): exposure_value = 'NULL' flat_table.add_value( value, aggregation_id=aggregation_value, hazard_id=hazard_value, exposure_class=exposure_value ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = feature[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, aggregation_id=aggregation_value, hazard_id=hazard_value ) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] classification = hazard_keywords['classification'] for area in aggregate_hazard.getFeatures(request): aggregation_value = area[aggregation_id] feature_hazard_id = area[hazard_id] if not feature_hazard_id or isinstance( feature_hazard_id, QPyNullVariant): feature_hazard_id = not_exposed_class['key'] feature_hazard_value = area[hazard_class] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value( aggregation_id=aggregation_value, hazard_id=feature_hazard_id, exposure_class=val ) total += sum aggregate_hazard.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( classification=classification, hazard_class=feature_hazard_value) affected = tr(unicode(affected)) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure), affected) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure) + 1, total) for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( aggregation_id=aggregation_value, hazard_id=feature_hazard_id ) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure) + 2 + i, value) aggregate_hazard.commitChanges() aggregate_hazard.keywords['title'] = ( layer_purpose_aggregate_hazard_impacted['name']) if qgis_version() >= 21800: aggregate_hazard.setName(aggregate_hazard.keywords['title']) else: aggregate_hazard.setLayerName(aggregate_hazard.keywords['title']) aggregate_hazard.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) check_layer(aggregate_hazard) return aggregate_hazard
def aggregation_summary(aggregate_hazard, aggregation, callback=None): """Compute the summary from the aggregate hazard to the analysis layer. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | aggr_id | aggr_name | Output layer : | aggr_id | aggr_name | count of affected features per exposure type :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param aggregation: The aggregation vector layer where to write statistics. :type aggregation: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new aggregation layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_2_aggregation_steps['output_layer_name'] processing_step = summary_2_aggregation_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = aggregation.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, ] check_inputs(target_compulsory_fields, target_fields) # Missing exposure_count_field source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, affected_field, ] check_inputs(source_compulsory_fields, source_fields) pattern = exposure_count_field['key'] pattern = pattern.replace('%s', '') unique_exposure = read_dynamic_inasafe_field(source_fields, exposure_count_field) absolute_values = create_absolute_values_structure(aggregate_hazard, ['aggregation_id']) flat_table = FlatTable('aggregation_id', 'exposure_class') aggregation_index = source_fields[aggregation_id_field['key']] # We want to loop over affected features only. request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) expression = '\"%s\" = \'%s\'' % (affected_field['field_name'], tr('True')) request.setFilterExpression(expression) for area in aggregate_hazard.getFeatures(request): for key, name_field in source_fields.iteritems(): if key.endswith(pattern): aggregation_id = area[aggregation_index] exposure_class = key.replace(pattern, '') value = area[name_field] flat_table.add_value(value, aggregation_id=aggregation_id, exposure_class=exposure_class) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, aggregation_id=area[aggregation_index], ) shift = aggregation.fields().count() aggregation.startEditing() add_fields(aggregation, absolute_values, [total_affected_field], unique_exposure, affected_exposure_count_field) aggregation_index = target_fields[aggregation_id_field['key']] request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregation.getFeatures(request): aggregation_value = area[aggregation_index] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value(aggregation_id=aggregation_value, exposure_class=val) total += sum aggregation.changeAttributeValue(area.id(), shift + i, sum) aggregation.changeAttributeValue(area.id(), shift + len(unique_exposure), total) for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value(aggregation_id=aggregation_value, ) target_index = shift + len(unique_exposure) + 1 + i aggregation.changeAttributeValue(area.id(), target_index, value) aggregation.commitChanges() aggregation.keywords['title'] = layer_purpose_aggregation_summary['name'] if qgis_version() >= 21800: aggregation.setName(aggregation.keywords['title']) else: aggregation.setLayerName(aggregation.keywords['title']) aggregation.keywords['layer_purpose'] = ( layer_purpose_aggregation_summary['key']) check_layer(aggregation) return aggregation
def aggregation_summary(aggregate_hazard, aggregation, callback=None): """Compute the summary from the aggregate hazard to the analysis layer. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | aggr_id | aggr_name | Output layer : | aggr_id | aggr_name | count of affected features per exposure type :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param aggregation: The aggregation vector layer where to write statistics. :type aggregation: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new aggregation layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_2_aggregation_steps['output_layer_name'] processing_step = summary_2_aggregation_steps['step_name'] source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = aggregation.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, ] check_inputs(target_compulsory_fields, target_fields) # Missing exposure_count_field source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, affected_field, ] check_inputs(source_compulsory_fields, source_fields) pattern = exposure_count_field['key'] pattern = pattern.replace('%s', '') unique_exposure = read_dynamic_inasafe_field( source_fields, exposure_count_field) absolute_values = create_absolute_values_structure( aggregate_hazard, ['aggregation_id']) flat_table = FlatTable('aggregation_id', 'exposure_class') aggregation_index = source_fields[aggregation_id_field['key']] # We want to loop over affected features only. request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) expression = '\"%s\" = \'%s\'' % ( affected_field['field_name'], tr('True')) request.setFilterExpression(expression) for area in aggregate_hazard.getFeatures(request): for key, name_field in source_fields.iteritems(): if key.endswith(pattern): aggregation_id = area[aggregation_index] exposure_class = key.replace(pattern, '') value = area[name_field] flat_table.add_value( value, aggregation_id=aggregation_id, exposure_class=exposure_class ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = area[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, aggregation_id=area[aggregation_index], ) shift = aggregation.fields().count() aggregation.startEditing() add_fields( aggregation, absolute_values, [total_affected_field], unique_exposure, affected_exposure_count_field) aggregation_index = target_fields[aggregation_id_field['key']] request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregation.getFeatures(request): aggregation_value = area[aggregation_index] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value( aggregation_id=aggregation_value, exposure_class=val ) total += sum aggregation.changeAttributeValue(area.id(), shift + i, sum) aggregation.changeAttributeValue( area.id(), shift + len(unique_exposure), total) for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( aggregation_id=aggregation_value, ) target_index = shift + len(unique_exposure) + 1 + i aggregation.changeAttributeValue( area.id(), target_index, value) aggregation.commitChanges() aggregation.keywords['title'] = layer_purpose_aggregation_summary['name'] if qgis_version() >= 21800: aggregation.setName(aggregation.keywords['title']) else: aggregation.setLayerName(aggregation.keywords['title']) aggregation.keywords['layer_purpose'] = ( layer_purpose_aggregation_summary['key']) check_layer(aggregation) return aggregation
def analysis_summary(aggregate_hazard, analysis): """Compute the summary from the aggregate hazard to analysis. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | analysis_name | Output layer : | analysis_name | count_hazard_class | affected_count | total | :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param analysis: The target vector layer where to write statistics. :type analysis: QgsVectorLayer :return: The new target layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = analysis.keywords['inasafe_fields'] target_compulsory_fields = [ analysis_name_field, ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, total_field ] check_inputs(source_compulsory_fields, source_fields) absolute_values = create_absolute_values_structure(aggregate_hazard, ['all']) hazard_class = source_fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fields().lookupField(hazard_class) unique_hazard = list(aggregate_hazard.uniqueValues(hazard_class_index)) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] hazard = hazard_keywords['hazard'] classification = hazard_keywords['classification'] exposure_keywords = aggregate_hazard.keywords['exposure_keywords'] exposure = exposure_keywords['exposure'] total = source_fields[total_field['key']] flat_table = FlatTable('hazard_class') # First loop over the aggregate_hazard layer request = QgsFeatureRequest() request.setSubsetOfAttributes([hazard_class, total], aggregate_hazard.fields()) request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregate_hazard.getFeatures(): hazard_value = area[hazard_class_index] value = area[total] if (value == '' or value is None or isnan(value) or (hasattr(value, 'isNull') and value.isNull())): # For isnan, see ticket #3812 value = 0 if (hazard_value == '' or hazard_value is None or (hasattr(hazard_value, 'isNull') and hazard_value.isNull())): hazard_value = 'NULL' flat_table.add_value(value, hazard_class=hazard_value) # We summarize every absolute values. for field, field_definition in list(absolute_values.items()): value = area[field] if (value == '' or value is None or (hasattr(value, 'isNull') and value.isNull())): value = 0 field_definition[0].add_value(value, all='all') analysis.startEditing() shift = analysis.fields().count() counts = [ total_affected_field, total_not_affected_field, total_exposed_field, total_not_exposed_field, total_field ] dynamic_structure = [ [hazard_count_field, unique_hazard], ] add_fields(analysis, absolute_values, counts, dynamic_structure) affected_sum = 0 not_affected_sum = 0 not_exposed_sum = 0 # Summarization summary_values = {} for key, summary_rule in list(summary_rules.items()): input_field = summary_rule['input_field'] case_field = summary_rule['case_field'] if aggregate_hazard.fields().lookupField(input_field['field_name']) \ == -1: continue if aggregate_hazard.fields().lookupField(case_field['field_name']) \ == -1: continue summary_value = 0 for area in aggregate_hazard.getFeatures(): case_value = area[case_field['field_name']] if case_value in summary_rule['case_values']: summary_value += area[input_field['field_name']] summary_values[key] = summary_value for area in analysis.getFeatures(request): total = 0 for i, val in enumerate(unique_hazard): if (val == '' or val is None or (hasattr(val, 'isNull') and val.isNull())): val = 'NULL' sum = flat_table.get_value(hazard_class=val) total += sum analysis.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( exposure=exposure, hazard=hazard, classification=classification, hazard_class=val) if affected == not_exposed_class['key']: not_exposed_sum += sum elif affected: affected_sum += sum else: not_affected_sum += sum # Total Affected field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard), affected_sum) # Total Not affected field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 1, not_affected_sum) # Total Exposed field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 2, total - not_exposed_sum) # Total Not exposed field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 3, not_exposed_sum) # Total field analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 4, total) # Any absolute postprocessors for i, field in enumerate(absolute_values.values()): value = field[0].get_value(all='all') analysis.changeAttributeValue(area.id(), shift + len(unique_hazard) + 5 + i, value) # Summarizer of custom attributes for key, summary_value in list(summary_values.items()): summary_field = summary_rules[key]['summary_field'] field = create_field_from_definition(summary_field) analysis.addAttribute(field) field_index = analysis.fields().lookupField(field.name()) # noinspection PyTypeChecker analysis.keywords['inasafe_fields'][summary_field['key']] = ( summary_field['field_name']) analysis.changeAttributeValue(area.id(), field_index, summary_value) # Sanity check ± 1 to the result. Disabled for now as it seems ± 1 is not # enough. ET 13/02/17 # total_computed = ( # affected_sum + not_affected_sum + not_exposed_sum) # if not -1 < (total_computed - total) < 1: # raise ComputationError analysis.commitChanges() analysis.keywords['title'] = layer_purpose_analysis_impacted['name'] if qgis_version() >= 21600: analysis.setName(analysis.keywords['title']) else: analysis.setLayerName(analysis.keywords['title']) analysis.keywords['layer_purpose'] = layer_purpose_analysis_impacted['key'] check_layer(analysis) return analysis
def aggregate_hazard_summary(impact, aggregate_hazard, callback=None): """Compute the summary from the source layer to the aggregate_hazard layer. Source layer : |exp_id|exp_class|haz_id|haz_class|aggr_id|aggr_name|affected|extra*| Target layer : | aggr_id | aggr_name | haz_id | haz_class | extra* | Output layer : |aggr_id| aggr_name|haz_id|haz_class|affected|extra*|count ber exposure*| :param impact: The layer to aggregate vector layer. :type impact: QgsVectorLayer :param aggregate_hazard: The aggregate_hazard vector layer where to write statistics. :type aggregate_hazard: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The new aggregate_hazard layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = summary_1_aggregate_hazard_steps['output_layer_name'] processing_step = summary_1_aggregate_hazard_steps['step_name'] source_fields = impact.keywords['inasafe_fields'] target_fields = aggregate_hazard.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ exposure_id_field, exposure_class_field, aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(source_compulsory_fields, source_fields) aggregation_id = target_fields[aggregation_id_field['key']] hazard_id = target_fields[hazard_id_field['key']] hazard_class = target_fields[hazard_class_field['key']] exposure_class = source_fields[exposure_class_field['key']] exposure_class_index = impact.fieldNameIndex(exposure_class) unique_exposure = impact.uniqueValues(exposure_class_index) fields = ['aggregation_id', 'hazard_id'] absolute_values = create_absolute_values_structure(impact, fields) # We need to know what kind of exposure we are going to count. # the size, or the number of features or population. field_index = report_on_field(impact) aggregate_hazard.startEditing() shift = aggregate_hazard.fields().count() add_fields( aggregate_hazard, absolute_values, [affected_field, total_field], unique_exposure, exposure_count_field ) flat_table = FlatTable('aggregation_id', 'hazard_id', 'exposure_class') request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) LOGGER.debug('Computing the aggregate hazard summary.') for feature in impact.getFeatures(request): # Field_index can be equal to 0. if field_index is not None: value = feature[field_index] else: value = 1 aggregation_value = feature[aggregation_id] hazard_value = feature[hazard_id] if not hazard_value or isinstance(hazard_value, QPyNullVariant): hazard_value = not_exposed_class['key'] exposure_value = feature[exposure_class] if not exposure_value or isinstance(exposure_value, QPyNullVariant): exposure_value = 'NULL' flat_table.add_value( value, aggregation_id=aggregation_value, hazard_id=hazard_value, exposure_class=exposure_value ) # We summarize every absolute values. for field, field_definition in absolute_values.iteritems(): value = feature[field] if not value or isinstance(value, QPyNullVariant): value = 0 field_definition[0].add_value( value, aggregation_id=aggregation_value, hazard_id=hazard_value ) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] classification = hazard_keywords['classification'] for area in aggregate_hazard.getFeatures(request): aggregation_value = area[aggregation_id] feature_hazard_id = area[hazard_id] if not feature_hazard_id or isinstance( feature_hazard_id, QPyNullVariant): feature_hazard_id = not_exposed_class['key'] feature_hazard_value = area[hazard_class] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value( aggregation_id=aggregation_value, hazard_id=feature_hazard_id, exposure_class=val ) total += sum aggregate_hazard.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( classification=classification, hazard_class=feature_hazard_value) affected = tr(unicode(affected)) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure), affected) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure) + 1, total) for i, field in enumerate(absolute_values.itervalues()): value = field[0].get_value( aggregation_id=aggregation_value, hazard_id=feature_hazard_id ) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure) + 2 + i, value) aggregate_hazard.commitChanges() aggregate_hazard.keywords['title'] = ( layer_purpose_aggregate_hazard_impacted['name']) if qgis_version() >= 21800: aggregate_hazard.setName(aggregate_hazard.keywords['title']) else: aggregate_hazard.setLayerName(aggregate_hazard.keywords['title']) aggregate_hazard.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) check_layer(aggregate_hazard) return aggregate_hazard
def analysis_summary(aggregate_hazard, analysis): """Compute the summary from the aggregate hazard to analysis. Source layer : | haz_id | haz_class | aggr_id | aggr_name | total_feature | Target layer : | analysis_name | Output layer : | analysis_name | count_hazard_class | affected_count | total | :param aggregate_hazard: The layer to aggregate vector layer. :type aggregate_hazard: QgsVectorLayer :param analysis: The target vector layer where to write statistics. :type analysis: QgsVectorLayer :return: The new target layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ source_fields = aggregate_hazard.keywords['inasafe_fields'] target_fields = analysis.keywords['inasafe_fields'] target_compulsory_fields = [ analysis_name_field, ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field, total_field ] check_inputs(source_compulsory_fields, source_fields) absolute_values = create_absolute_values_structure( aggregate_hazard, ['all']) hazard_class = source_fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fields().lookupField(hazard_class) unique_hazard = list(aggregate_hazard.uniqueValues(hazard_class_index)) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] hazard = hazard_keywords['hazard'] classification = hazard_keywords['classification'] exposure_keywords = aggregate_hazard.keywords['exposure_keywords'] exposure = exposure_keywords['exposure'] total = source_fields[total_field['key']] flat_table = FlatTable('hazard_class') # First loop over the aggregate_hazard layer request = QgsFeatureRequest() request.setSubsetOfAttributes( [hazard_class, total], aggregate_hazard.fields()) request.setFlags(QgsFeatureRequest.NoGeometry) for area in aggregate_hazard.getFeatures(): hazard_value = area[hazard_class_index] value = area[total] if (value == '' or value is None or isnan(value) or (hasattr(value, 'isNull') and value.isNull())): # For isnan, see ticket #3812 value = 0 if (hazard_value == '' or hazard_value is None or (hasattr(hazard_value, 'isNull') and hazard_value.isNull())): hazard_value = 'NULL' flat_table.add_value( value, hazard_class=hazard_value ) # We summarize every absolute values. for field, field_definition in list(absolute_values.items()): value = area[field] if (value == '' or value is None or (hasattr(value, 'isNull') and value.isNull())): value = 0 field_definition[0].add_value( value, all='all' ) analysis.startEditing() shift = analysis.fields().count() counts = [ total_affected_field, total_not_affected_field, total_exposed_field, total_not_exposed_field, total_field] dynamic_structure = [ [hazard_count_field, unique_hazard], ] add_fields( analysis, absolute_values, counts, dynamic_structure) affected_sum = 0 not_affected_sum = 0 not_exposed_sum = 0 # Summarization summary_values = {} for key, summary_rule in list(summary_rules.items()): input_field = summary_rule['input_field'] case_field = summary_rule['case_field'] if aggregate_hazard.fields().lookupField(input_field['field_name']) \ == -1: continue if aggregate_hazard.fields().lookupField(case_field['field_name']) \ == -1: continue summary_value = 0 for area in aggregate_hazard.getFeatures(): case_value = area[case_field['field_name']] if case_value in summary_rule['case_values']: summary_value += area[input_field['field_name']] summary_values[key] = summary_value for area in analysis.getFeatures(request): total = 0 for i, val in enumerate(unique_hazard): if (val == '' or val is None or (hasattr(val, 'isNull') and val.isNull())): val = 'NULL' sum = flat_table.get_value(hazard_class=val) total += sum analysis.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( exposure=exposure, hazard=hazard, classification=classification, hazard_class=val) if affected == not_exposed_class['key']: not_exposed_sum += sum elif affected: affected_sum += sum else: not_affected_sum += sum # Total Affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard), affected_sum) # Total Not affected field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 1, not_affected_sum) # Total Exposed field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 2, total - not_exposed_sum) # Total Not exposed field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 3, not_exposed_sum) # Total field analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 4, total) # Any absolute postprocessors for i, field in enumerate(absolute_values.values()): value = field[0].get_value( all='all' ) analysis.changeAttributeValue( area.id(), shift + len(unique_hazard) + 5 + i, value) # Summarizer of custom attributes for key, summary_value in list(summary_values.items()): summary_field = summary_rules[key]['summary_field'] field = create_field_from_definition(summary_field) analysis.addAttribute(field) field_index = analysis.fields().lookupField(field.name()) # noinspection PyTypeChecker analysis.keywords['inasafe_fields'][summary_field['key']] = ( summary_field['field_name']) analysis.changeAttributeValue( area.id(), field_index, summary_value) # Sanity check ± 1 to the result. Disabled for now as it seems ± 1 is not # enough. ET 13/02/17 # total_computed = ( # affected_sum + not_affected_sum + not_exposed_sum) # if not -1 < (total_computed - total) < 1: # raise ComputationError analysis.commitChanges() analysis.keywords['title'] = layer_purpose_analysis_impacted['name'] if qgis_version() >= 21600: analysis.setName(analysis.keywords['title']) else: analysis.setLayerName(analysis.keywords['title']) analysis.keywords['layer_purpose'] = layer_purpose_analysis_impacted['key'] check_layer(analysis) return analysis
def aggregate_hazard_summary(impact, aggregate_hazard): """Compute the summary from the source layer to the aggregate_hazard layer. Source layer : |exp_id|exp_class|haz_id|haz_class|aggr_id|aggr_name|affected|extra*| Target layer : | aggr_id | aggr_name | haz_id | haz_class | extra* | Output layer : |aggr_id| aggr_name|haz_id|haz_class|affected|extra*|count ber exposure*| :param impact: The layer to aggregate vector layer. :type impact: QgsVectorLayer :param aggregate_hazard: The aggregate_hazard vector layer where to write statistics. :type aggregate_hazard: QgsVectorLayer :return: The new aggregate_hazard layer with summary. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ source_fields = impact.keywords['inasafe_fields'] target_fields = aggregate_hazard.keywords['inasafe_fields'] target_compulsory_fields = [ aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(target_compulsory_fields, target_fields) source_compulsory_fields = [ exposure_id_field, exposure_class_field, aggregation_id_field, aggregation_name_field, hazard_id_field, hazard_class_field ] check_inputs(source_compulsory_fields, source_fields) aggregation_id = target_fields[aggregation_id_field['key']] hazard_id = target_fields[hazard_id_field['key']] hazard_class = target_fields[hazard_class_field['key']] exposure_class = source_fields[exposure_class_field['key']] exposure_class_index = impact.fields().lookupField(exposure_class) unique_exposure = list(impact.uniqueValues(exposure_class_index)) fields = ['aggregation_id', 'hazard_id'] absolute_values = create_absolute_values_structure(impact, fields) # We need to know what kind of exposure we are going to count. # the size, or the number of features or population. field_index = report_on_field(impact) aggregate_hazard.startEditing() shift = aggregate_hazard.fields().count() dynamic_structure = [ [exposure_count_field, unique_exposure], ] add_fields( aggregate_hazard, absolute_values, [affected_field, total_field], dynamic_structure, ) flat_table = FlatTable('aggregation_id', 'hazard_id', 'exposure_class') request = QgsFeatureRequest() request.setFlags(QgsFeatureRequest.NoGeometry) LOGGER.debug('Computing the aggregate hazard summary.') for feature in impact.getFeatures(request): # Field_index can be equal to 0. if field_index is not None: value = feature[field_index] else: value = 1 aggregation_value = feature[aggregation_id] hazard_value = feature[hazard_id] if (hazard_value is None or hazard_value == '' or (hasattr(hazard_value, 'isNull') and hazard_value.isNull())): hazard_value = not_exposed_class['key'] exposure_value = feature[exposure_class] if (exposure_value is None or exposure_value == '' or (hasattr(exposure_value, 'isNull') and exposure_value.isNull())): exposure_value = 'NULL' flat_table.add_value(value, aggregation_id=aggregation_value, hazard_id=hazard_value, exposure_class=exposure_value) # We summarize every absolute values. for field, field_definition in list(absolute_values.items()): value = feature[field] if (value == '' or value is None or (hasattr(value, 'isNull') and value.isNull())): value = 0 field_definition[0].add_value(value, aggregation_id=aggregation_value, hazard_id=hazard_value) hazard_keywords = aggregate_hazard.keywords['hazard_keywords'] hazard = hazard_keywords['hazard'] classification = hazard_keywords['classification'] exposure_keywords = impact.keywords['exposure_keywords'] exposure = exposure_keywords['exposure'] for area in aggregate_hazard.getFeatures(request): aggregation_value = area[aggregation_id] feature_hazard_id = area[hazard_id] if (feature_hazard_id == '' or feature_hazard_id is None or (hasattr(feature_hazard_id, 'isNull') and feature_hazard_id.isNull())): feature_hazard_id = not_exposed_class['key'] feature_hazard_value = area[hazard_class] total = 0 for i, val in enumerate(unique_exposure): sum = flat_table.get_value(aggregation_id=aggregation_value, hazard_id=feature_hazard_id, exposure_class=val) total += sum aggregate_hazard.changeAttributeValue(area.id(), shift + i, sum) affected = post_processor_affected_function( exposure=exposure, hazard=hazard, classification=classification, hazard_class=feature_hazard_value) affected = tr(str(affected)) aggregate_hazard.changeAttributeValue(area.id(), shift + len(unique_exposure), affected) aggregate_hazard.changeAttributeValue(area.id(), shift + len(unique_exposure) + 1, total) for i, field in enumerate(absolute_values.values()): value = field[0].get_value(aggregation_id=aggregation_value, hazard_id=feature_hazard_id) aggregate_hazard.changeAttributeValue( area.id(), shift + len(unique_exposure) + 2 + i, value) aggregate_hazard.commitChanges() aggregate_hazard.keywords['title'] = ( layer_purpose_aggregate_hazard_impacted['name']) if qgis_version() >= 21800: aggregate_hazard.setName(aggregate_hazard.keywords['title']) else: aggregate_hazard.setLayerName(aggregate_hazard.keywords['title']) aggregate_hazard.keywords['layer_purpose'] = ( layer_purpose_aggregate_hazard_impacted['key']) aggregate_hazard.keywords['exposure_keywords'] = impact.keywords.copy() check_layer(aggregate_hazard) return aggregate_hazard