def test_analysis_summary(self): """Test we can aggregate the aggregate hazard to the analysis.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard_summary.geojson') aggregate_hazard.keywords['hazard_keywords'] = { 'classification': 'generic_hazard_classes' } analysis = load_test_vector_layer('gisv4', 'intermediate', 'analysis.geojson', clone=True) number_of_fields = analysis.fields().count() layer = analysis_summary(aggregate_hazard, analysis) check_inasafe_fields(layer) fields = aggregate_hazard.keywords['inasafe_fields'] hazard_class = fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) # expected number of fields: # - one field for each hazard class # - 2 fields for analysis id and analysis name # - 4 fields for total affected, not_affected, not exposed and total self.assertEqual(layer.fields().count(), len(unique_hazard) + number_of_fields + 4)
def test_aggregation_summary(self): """Test we can aggregate the aggregate hazard to the aggregation.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard_summary.geojson') aggregation = load_test_vector_layer( 'gisv4', 'aggregation', 'aggregation_cleaned.geojson', clone=True) number_of_fields = aggregation.fields().count() layer = aggregation_summary(aggregate_hazard, aggregation) check_inasafe_fields(layer) # I need the number of unique exposure pattern = exposure_count_field['key'] pattern = pattern.replace('%s', '') unique_exposure = [] inasafe_fields = aggregate_hazard.keywords['inasafe_fields'] for key, name_field in inasafe_fields.iteritems(): if key.endswith(pattern): unique_exposure.append(key.replace(pattern, '')) self.assertEqual( layer.fields().count(), len(unique_exposure) + number_of_fields + 1 )
def test_analysis_earthquake_summary(self): """Test we can compute summary after an EQ on population.""" hazard = load_test_raster_layer('gisv4', 'hazard', 'earthquake.asc') exposure = load_test_raster_layer( 'gisv4', 'exposure', 'raster', 'population.asc') aggregation = load_test_vector_layer( 'gisv4', 'aggregation', 'small_grid.geojson') impact_function = ImpactFunction() impact_function.hazard = hazard impact_function.exposure = exposure impact_function.aggregation = aggregation status, message = impact_function.prepare() self.assertEqual(PREPARE_SUCCESS, status, message) status, message = impact_function.run() self.assertEqual(ANALYSIS_SUCCESS, status, message) layer = impact_function.analysis_impacted classification = hazard.keywords['classification'] classes = definition(classification)['classes'] for hazard_class in classes: field_name = hazard_count_field['field_name'] % hazard_class['key'] message = '%s is not found in the EQ summary layer.' % field_name self.assertNotEqual(-1, layer.fieldNameIndex(field_name), message) check_inasafe_fields(impact_function.analysis_impacted) check_inasafe_fields(impact_function.aggregation_summary)
def test_exposure_summary_table(self): """Test we can produce the breakdown for the exposure type.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard_summary.geojson') aggregate_hazard.keywords['hazard_keywords'] = { 'classification': 'generic_hazard_classes' } # I need the number of unique exposure unique_exposure = read_dynamic_inasafe_field( aggregate_hazard.keywords['inasafe_fields'], exposure_count_field) # I need the number of unique hazard fields = aggregate_hazard.keywords['inasafe_fields'] hazard_class = fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) layer = exposure_summary_table(aggregate_hazard, None) check_inasafe_fields(layer) self.assertEqual(len(unique_exposure), layer.featureCount()) # We should have # one column per hazard # one for the exposure # one for total affected # one for total not affected # one for total not exposed # one for total self.assertEqual(layer.fields().count(), len(unique_hazard) + 5)
def test_analysis_earthquake_summary(self): """Test we can compute summary after an EQ on population.""" hazard = load_test_raster_layer('gisv4', 'hazard', 'earthquake.asc') exposure = load_test_raster_layer('gisv4', 'exposure', 'raster', 'population.asc') aggregation = load_test_vector_layer('gisv4', 'aggregation', 'small_grid.geojson') impact_function = ImpactFunction() impact_function.hazard = hazard impact_function.exposure = exposure impact_function.aggregation = aggregation status, message = impact_function.prepare() self.assertEqual(PREPARE_SUCCESS, status, message) status, message = impact_function.run() self.assertEqual(ANALYSIS_SUCCESS, status, message) layer = impact_function.analysis_impacted classification = hazard.keywords['classification'] classes = definition(classification)['classes'] for hazard_class in classes: field_name = hazard_count_field['field_name'] % hazard_class['key'] message = '%s is not found in the EQ summary layer.' % field_name self.assertNotEqual(-1, layer.fieldNameIndex(field_name), message) check_inasafe_fields(impact_function.analysis_impacted) check_inasafe_fields(impact_function.aggregation_summary)
def test_exposure_summary_table_productivity(self): """Test we can produce the breakdown for the exposure type.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'summaries', 'land_cover_aggregate_hazard_impacted.geojson') aggregate_hazard.keywords['hazard_keywords'] = { 'hazard': 'generic', 'classification': 'generic_hazard_classes' } aggregate_hazard.keywords['exposure_keywords'] = { 'exposure': 'land_cover' } exposure_summary = load_test_vector_layer( 'gisv4', 'intermediate', 'summaries', 'land_cover_exposure_summary.geojson' ) # I need the number of unique exposure unique_exposure = read_dynamic_inasafe_field( aggregate_hazard.keywords['inasafe_fields'], exposure_count_field) # I need the number of unique hazard fields = aggregate_hazard.keywords['inasafe_fields'] hazard_class = fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fields().lookupField(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) layer = exposure_summary_table(aggregate_hazard, exposure_summary) check_inasafe_fields(layer) self.assertEqual(len(unique_exposure), layer.featureCount()) # We should have # one column per hazard # 1. one for the exposure # 2. one for total affected # 3. one for total not affected # 4. one for total not exposed # 5. one for total # 6. one for affected productivity # 7. one for affected production cost # 8. one for affected production value self.assertEqual(layer.fields().count(), len(unique_hazard) + 8)
def test_exposure_summary_table_productivity(self): """Test we can produce the breakdown for the exposure type.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'summaries', 'land_cover_aggregate_hazard_impacted.geojson') aggregate_hazard.keywords['hazard_keywords'] = { 'classification': 'generic_hazard_classes' } exposure_summary = load_test_vector_layer( 'gisv4', 'intermediate', 'summaries', 'land_cover_exposure_summary.geojson' ) # I need the number of unique exposure unique_exposure = read_dynamic_inasafe_field( aggregate_hazard.keywords['inasafe_fields'], exposure_count_field) # I need the number of unique hazard fields = aggregate_hazard.keywords['inasafe_fields'] hazard_class = fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) layer = exposure_summary_table(aggregate_hazard, exposure_summary) check_inasafe_fields(layer) self.assertEqual(len(unique_exposure), layer.featureCount()) # We should have # one column per hazard # one for the exposure # one for total affected # one for total not affected # one for total not exposed # one for total # one for affected productivity # one for affected production cost # one for affected production value self.assertEqual(layer.fields().count(), len(unique_hazard) + 8)
def test_impact_summary(self): """Test we can aggregate the impact to the aggregate hazard.""" impact = load_test_vector_layer( 'gisv4', 'impacts', 'building-points-classified-vector.geojson') aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard.geojson', clone=True) aggregate_hazard.keywords['hazard_keywords'] = { 'hazard': 'generic', 'classification': 'generic_hazard_classes' } impact.keywords['classification'] = { 'classification': 'generic_structure_classes' } impact.keywords['exposure_keywords'] = { 'exposure': 'structure' } number_of_fields = aggregate_hazard.fields().count() layer = aggregate_hazard_summary(impact, aggregate_hazard) self.assertIn(total_field['key'], layer.keywords['inasafe_fields']) check_inasafe_fields(layer) fields = impact.keywords['inasafe_fields'] exposure_class = fields[exposure_class_field['key']] exposure_class_index = impact.fields().lookupField(exposure_class) unique_exposure = impact.uniqueValues(exposure_class_index) # One field per exposure type # Number of previous fields in the layer # 3 : 1 fields for absolute values, 2 fields for affected and total. self.assertEqual( layer.fields().count(), len(unique_exposure) + number_of_fields + 3 )
def test_impact_summary(self): """Test we can aggregate the impact to the aggregate hazard.""" impact = load_test_vector_layer( 'gisv4', 'impacts', 'building-points-classified-vector.geojson') aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard.geojson', clone=True) aggregate_hazard.keywords['hazard_keywords'] = { 'classification': 'generic_hazard_classes' } impact.keywords['classification'] = { 'classification': 'generic_structure_classes' } number_of_fields = aggregate_hazard.fields().count() layer = aggregate_hazard_summary(impact, aggregate_hazard) self.assertIn(total_field['key'], layer.keywords['inasafe_fields']) check_inasafe_fields(layer) fields = impact.keywords['inasafe_fields'] exposure_class = fields[exposure_class_field['key']] exposure_class_index = impact.fieldNameIndex(exposure_class) unique_exposure = impact.uniqueValues(exposure_class_index) # One field per exposure type # Number of previous fields in the layer # 3 : 1 fields for absolute values, 2 fields for affected and total. self.assertEqual( layer.fields().count(), len(unique_exposure) + number_of_fields + 3 )
def test_analysis_summary(self): """Test we can aggregate the aggregate hazard to the analysis.""" aggregate_hazard = load_test_vector_layer( 'gisv4', 'intermediate', 'aggregate_classified_hazard_summary.geojson') aggregate_hazard.keywords['hazard_keywords'] = { 'classification': 'generic_hazard_classes' } analysis = load_test_vector_layer( 'gisv4', 'intermediate', 'analysis.geojson', clone=True) number_of_fields = analysis.fields().count() layer = analysis_summary(aggregate_hazard, analysis) check_inasafe_fields(layer) fields = aggregate_hazard.keywords['inasafe_fields'] hazard_class = fields[hazard_class_field['key']] hazard_class_index = aggregate_hazard.fieldNameIndex(hazard_class) unique_hazard = aggregate_hazard.uniqueValues(hazard_class_index) # expected number of fields: # - one field for each hazard class # - 2 fields for analysis id and analysis name # - 4 fields for total affected, not_affected, not exposed and total self.assertEqual( layer.fields().count(), len(unique_hazard) + number_of_fields + 4 )
def run_scenario(scenario, use_debug=False): """Run scenario. :param scenario: Dictionary of hazard, exposure, and aggregation. :type scenario: dict :param use_debug: If we should use debug_mode when we run the scenario. :type use_debug: bool :returns: Tuple(status, Flow dictionary, outputs). :rtype: list """ if os.path.exists(scenario['exposure']): exposure_path = scenario['exposure'] elif os.path.exists(standard_data_path('exposure', scenario['exposure'])): exposure_path = standard_data_path('exposure', scenario['exposure']) elif os.path.exists( standard_data_path(*(scenario['exposure'].split('/')))): exposure_path = standard_data_path(*(scenario['exposure'].split('/'))) else: raise IOError('No exposure file') if os.path.exists(scenario['hazard']): hazard_path = scenario['hazard'] elif os.path.exists(standard_data_path('hazard', scenario['hazard'])): hazard_path = standard_data_path('hazard', scenario['hazard']) elif os.path.exists(standard_data_path(*(scenario['hazard'].split('/')))): hazard_path = standard_data_path(*(scenario['hazard'].split('/'))) else: raise IOError('No hazard file') if not scenario['aggregation']: aggregation_path = None else: if os.path.exists(scenario['aggregation']): aggregation_path = scenario['aggregation'] elif os.path.exists( standard_data_path('aggregation', scenario['aggregation'])): aggregation_path = standard_data_path('aggregation', scenario['aggregation']) elif os.path.exists( standard_data_path(*(scenario['aggregation'].split('/')))): aggregation_path = standard_data_path( *(scenario['aggregation'].split('/'))) else: raise IOError('No aggregation file') impact_function = ImpactFunction() impact_function.debug_mode = use_debug layer = QgsVectorLayer(hazard_path, 'Hazard', 'ogr') if not layer.isValid(): layer = QgsRasterLayer(hazard_path, 'Hazard') impact_function.hazard = layer layer = QgsVectorLayer(exposure_path, 'Exposure', 'ogr') if not layer.isValid(): layer = QgsRasterLayer(exposure_path, 'Exposure') impact_function.exposure = layer if aggregation_path: impact_function.aggregation = QgsVectorLayer(aggregation_path, 'Aggregation', 'ogr') status, message = impact_function.prepare() if status != 0: return status, message, None status, message = impact_function.run() if status != 0: return status, message, None for layer in impact_function.outputs: if layer.type() == QgsMapLayer.VectorLayer: check_inasafe_fields(layer) return status, impact_function.state, impact_function.outputs
def run_scenario(scenario, use_debug=False): """Run scenario. :param scenario: Dictionary of hazard, exposure, and aggregation. :type scenario: dict :param use_debug: If we should use debug_mode when we run the scenario. :type use_debug: bool :returns: Tuple(status, Flow dictionary, outputs). :rtype: list """ if os.path.exists(scenario['exposure']): exposure_path = scenario['exposure'] elif os.path.exists(standard_data_path('exposure', scenario['exposure'])): exposure_path = standard_data_path('exposure', scenario['exposure']) elif os.path.exists( standard_data_path(*(scenario['exposure'].split('/')))): exposure_path = standard_data_path(*(scenario['exposure'].split('/'))) else: raise IOError('No exposure file') if os.path.exists(scenario['hazard']): hazard_path = scenario['hazard'] elif os.path.exists(standard_data_path('hazard', scenario['hazard'])): hazard_path = standard_data_path('hazard', scenario['hazard']) elif os.path.exists(standard_data_path(*(scenario['hazard'].split('/')))): hazard_path = standard_data_path(*(scenario['hazard'].split('/'))) else: raise IOError('No hazard file') if not scenario['aggregation']: aggregation_path = None else: if os.path.exists(scenario['aggregation']): aggregation_path = scenario['aggregation'] elif os.path.exists(standard_data_path( 'aggregation', scenario['aggregation'])): aggregation_path = standard_data_path( 'aggregation', scenario['aggregation']) elif os.path.exists( standard_data_path(*(scenario['aggregation'].split('/')))): aggregation_path = standard_data_path( *(scenario['aggregation'].split('/'))) else: raise IOError('No aggregation file') impact_function = ImpactFunction() impact_function.debug_mode = use_debug layer = QgsVectorLayer(hazard_path, 'Hazard', 'ogr') if not layer.isValid(): layer = QgsRasterLayer(hazard_path, 'Hazard') impact_function.hazard = layer layer = QgsVectorLayer(exposure_path, 'Exposure', 'ogr') if not layer.isValid(): layer = QgsRasterLayer(exposure_path, 'Exposure') impact_function.exposure = layer if aggregation_path: impact_function.aggregation = QgsVectorLayer( aggregation_path, 'Aggregation', 'ogr') status, message = impact_function.prepare() if status != 0: return status, message, None status, message = impact_function.run() if status != 0: return status, message, None for layer in impact_function.outputs: if layer.type() == QgsMapLayer.VectorLayer: check_inasafe_fields(layer) return status, impact_function.state, impact_function.outputs
def check_input_layer(layer, purpose): """Function to check if the layer is valid. The function will also set the monkey patching if needed. :param layer: The layer to test. :type layer: QgsMapLayer :param purpose: The expected purpose of the layer. :type purpose: basestring :return: A tuple with the status of the layer and an error message if needed. The status is 0 if everything was fine. The status is 1 if the client should fix something. :rtype: (int, m.Message) """ if not layer.isValid(): title = tr('The {purpose} layer is invalid').format(purpose=purpose) content = tr('The impact function needs a {exposure} layer to run. ' 'You must provide a valid {exposure} layer.').format( purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message # We should read it using KeywordIO for the very beginning. To avoid # get the modified keywords in the patching. try: keywords = KeywordIO().read_keywords(layer) except NoKeywordsFoundError: title = tr('The {purpose} layer does not have keywords.').format( purpose=purpose) content = tr( 'The {purpose} layer does not have keywords. Use the wizard ' 'to assign keywords to the layer.').format(purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message if keywords.get('layer_purpose') != purpose: title = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) content = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message version = keywords.get(inasafe_keyword_version_key) supported = is_keyword_version_supported(version) if not supported: parameters = { 'version': inasafe_keyword_version, 'source': layer.publicSource() } title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr('The layer {source} must be updated to {version}.' ).format(**parameters) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message layer.keywords = keywords if is_vector_layer(layer): try: check_inasafe_fields(layer, keywords_only=True) except InvalidLayerError: title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated with the keyword ' 'wizard. Your fields which have been set in the keywords ' 'previously are not matching your layer.').format( source=layer.publicSource()) message = generate_input_error_message(title, m.Paragraph(content)) del layer.keywords return PREPARE_FAILED_BAD_INPUT, message return PREPARE_SUCCESS, None
def check_input_layer(layer, purpose): """Function to check if the layer is valid. The function will also set the monkey patching if needed. :param layer: The layer to test. :type layer: QgsMapLayer :param purpose: The expected purpose of the layer. :type purpose: basestring :return: A tuple with the status of the layer and an error message if needed. The status is 0 if everything was fine. The status is 1 if the client should fix something. :rtype: (int, m.Message) """ if not layer.isValid(): title = tr( 'The {purpose} layer is invalid').format(purpose=purpose) content = tr( 'The impact function needs a {exposure} layer to run. ' 'You must provide a valid {exposure} layer.').format( purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message # We should read it using KeywordIO for the very beginning. To avoid # get the modified keywords in the patching. try: keywords = KeywordIO().read_keywords(layer) except NoKeywordsFoundError: title = tr( 'The {purpose} layer does not have keywords.').format( purpose=purpose) content = tr( 'The {purpose} layer does not have keywords. Use the wizard ' 'to assign keywords to the layer.').format(purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message if keywords.get('layer_purpose') != purpose: title = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) content = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message version = keywords.get(inasafe_keyword_version_key) supported = is_keyword_version_supported(version) if not supported: parameters = { 'version': inasafe_keyword_version, 'source': layer.publicSource() } title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated to {version}.').format( **parameters) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message layer.keywords = keywords if is_vector_layer(layer): try: check_inasafe_fields(layer, keywords_only=True) except InvalidLayerError: title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated with the keyword ' 'wizard. Your fields which have been set in the keywords ' 'previously are not matching your layer.').format( source=layer.publicSource()) message = generate_input_error_message( title, m.Paragraph(content)) del layer.keywords return PREPARE_FAILED_BAD_INPUT, message return PREPARE_SUCCESS, None