Example #1
0
    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_profiling(self):
        """Test running impact function on test data."""
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'building-points.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_FAILED_BAD_INPUT, status, message)
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)
        message = impact_function.performance_log_message().to_text()
        expected_result = get_control_text(
            'test-profiling-logs.txt')

        for line in expected_result:
            line = line.replace('\n', '')
            if line == '' or line == '-':
                continue
            self.assertIn(line, message)

        # Notes(IS): For some unknown reason I need to do this to make
        # test_provenance pass
        del hazard_layer
    def test_profiling(self):
        """Test running impact function on test data."""
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'building-points.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_FAILED_BAD_INPUT, status, message)
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)
        message = impact_function.performance_log_message().to_text()
        expected_result = get_control_text('test-profiling-logs.txt')

        for line in expected_result:
            line = line.replace('\n', '')
            if line == '' or line == '-':
                continue
            self.assertIn(line, message)

        # Notes(IS): For some unknown reason I need to do this to make
        # test_provenance pass
        del hazard_layer
Example #4
0
    def prepare_impact_function(self):
        """Create analysis as a representation of current situation of IFCW."""

        # Impact Functions
        impact_function = ImpactFunction()
        impact_function.callback = self.progress_callback

        # Layers
        impact_function.hazard = self.parent.hazard_layer
        impact_function.exposure = self.parent.exposure_layer
        aggregation = self.parent.aggregation_layer

        if aggregation:
            impact_function.aggregation = aggregation
            impact_function.use_selected_features_only = (
                setting('useSelectedFeaturesOnly', False, bool))
        else:
            mode = setting('analysis_extents_mode')
            if self.extent.user_extent:
                # This like a hack to transform a geometry to a rectangle.
                # self.extent.user_extent is a QgsGeometry.
                # impact_function.requested_extent needs a QgsRectangle.
                wkt = self.extent.user_extent.exportToWkt()
                impact_function.requested_extent = wkt_to_rectangle(wkt)
                impact_function.requested_extent_crs = self.extent.crs

            elif mode == HAZARD_EXPOSURE_VIEW:
                impact_function.requested_extent = (
                    self.iface.mapCanvas().extent())
                impact_function.requested_extent_crs = self.extent.crs

        # We don't have any checkbox in the wizard for the debug mode.
        impact_function.debug_mode = False

        return impact_function
    def test_old_fields_keywords(self):
        """The IF is not ready with we have some wrong inasafe_fields."""
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4',
                                                'exposure',
                                                'building-points.geojson',
                                                clone=True)
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()

        # The layer should be fine.
        self.assertEqual(PREPARE_SUCCESS, status, message)

        # Now, we remove one field
        exposure_layer.startEditing()
        field = exposure_layer.keywords['inasafe_fields'].values()[0]
        index = exposure_layer.fieldNameIndex(field)
        exposure_layer.deleteAttribute(index)
        exposure_layer.commitChanges()

        # It shouldn't be fine as we removed one field which
        # was in inasafe_fields
        status, message = impact_function.prepare()
        self.assertNotEqual(PREPARE_SUCCESS, status, message)
Example #6
0
    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_old_fields_keywords(self):
        """The IF is not ready with we have some wrong inasafe_fields."""
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'building-points.geojson',
            clone=True)
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()

        # The layer should be fine.
        self.assertEqual(PREPARE_SUCCESS, status, message)

        # Now, we remove one field
        exposure_layer.startEditing()
        field = exposure_layer.keywords['inasafe_fields'].values()[0]
        index = exposure_layer.fieldNameIndex(field)
        exposure_layer.deleteAttribute(index)
        exposure_layer.commitChanges()

        # It shouldn't be fine as we removed one field which
        # was in inasafe_fields
        status, message = impact_function.prepare()
        self.assertNotEqual(PREPARE_SUCCESS, status, message)
Example #8
0
def run_impact_function(cli_arguments):
    """Runs an analysis and delegates producing pdf and .geojson output layers.

    .. versionadded:: 3.2

    :param cli_arguments: User inputs.
    :type cli_arguments: CommandLineArguments
    """
    hazard = get_layer(cli_arguments.hazard, 'Hazard Layer')
    exposure = get_layer(cli_arguments.exposure, 'Exposure Layer')
    aggregation = None
    if cli_arguments.aggregation:
        aggregation = get_layer(cli_arguments.aggregation, 'Aggregation Layer')

    # Set up impact function
    impact_function = ImpactFunction()
    impact_function.hazard = hazard
    impact_function.exposure = exposure
    impact_function.aggregation = aggregation
    # Set the datastore
    impact_function.datastore = Folder(cli_arguments.output_dir)
    impact_function.datastore.default_vector_format = 'geojson'

    # Set the extent
    if cli_arguments.extent:
        impact_function.requested_extent_crs = \
            QgsCoordinateReferenceSystem(4326)
        try:
            impact_function.requested_extent = QgsRectangle(
                float(cli_arguments.extent[0]),
                float(cli_arguments.extent[1]),
                float(cli_arguments.extent[2]),
                float(cli_arguments.extent[3])
            )
        except AttributeError:
            print "Extent is not valid..."
            pass

    # Prepare impact function
    status, message = impact_function.prepare()
    if status != PREPARE_SUCCESS:
        print message.to_text()
        return status, message, None

    status, message = impact_function.run()
    if status != ANALYSIS_SUCCESS:
        print message.to_text()
        return status, message, None

    return status, message, impact_function
    def test_vector_post_minimum_needs_value_generation(self):
        """Test minimum needs postprocessors on vector exposure.

        Test with vector exposure data with population_count_field exists.

        Minimum needs postprocessors is defined to only generate values when
        exposure contains population data.
        """
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'tsunami_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'population.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        return_code, message = impact_function.run()

        self.assertEqual(return_code, ANALYSIS_SUCCESS, message)

        # minimum needs fields should exists in the results
        self._check_minimum_fields_exists(impact_function)

        expected_value = {
            u'population': 69,
            u'total': 9.0,
            u'minimum_needs__rice': 491,
            u'minimum_needs__clean_water': 11763,
            u'minimum_needs__toilets': 8,
            u'minimum_needs__drinking_water': 3072,
            u'minimum_needs__family_kits': 35,
            u'male': 34,
            u'female': 34,
            u'youth': 17,
            u'adult': 45,
            u'elderly': 6,
            u'total_affected': 6.0,
        }

        self._check_minimum_fields_value(expected_value, impact_function)
    def test_vector_post_minimum_needs_value_generation(self):
        """Test minimum needs postprocessors on vector exposure.

        Test with vector exposure data with population_count_field exists.

        Minimum needs postprocessors is defined to only generate values when
        exposure contains population data.
        """
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'tsunami_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'population.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        return_code, message = impact_function.run()

        self.assertEqual(return_code, ANALYSIS_SUCCESS, message)

        # minimum needs fields should exists in the results
        self._check_minimum_fields_exists(impact_function)

        expected_value = {
            u'population': 69,
            u'total': 9.0,
            u'minimum_needs__rice': 491,
            u'minimum_needs__clean_water': 11763,
            u'minimum_needs__toilets': 8,
            u'minimum_needs__drinking_water': 3072,
            u'minimum_needs__family_kits': 35,
            u'male': 34,
            u'female': 34,
            u'youth': 17,
            u'adult': 45,
            u'elderly': 6,
            u'total_affected': 6.0,
        }

        self._check_minimum_fields_value(expected_value, impact_function)
Example #11
0
def impact_function_setup(command_line_arguments,
                          hazard,
                          exposure,
                          aggregation=None):
    """Sets up an analysis object.

    .. versionadded:: 3.2

    :param command_line_arguments: User inputs.
    :type command_line_arguments: CommandLineArguments

    :param hazard: Hazard layer
    :type hazard: QgsLayer

    :param exposure: Exposure Layer
    :type exposure: QgsLayer

    :param aggregation: Aggregation Layer
    :type aggregation: QgsLayer

    :raises: Exception
    """
    # IF
    impact_function = ImpactFunction()

    impact_function.hazard = hazard
    impact_function.exposure = exposure
    impact_function.aggregation = aggregation
    impact_function.map_canvas = CANVAS
    # QSetting context
    settings = QSettings()
    crs = settings.value('inasafe/user_extent_crs', '', type=str)
    impact_function.requested_extent_crs = QgsCoordinateReferenceSystem(crs)
    try:
        impact_function.requested_extent = QgsRectangle(
            float(command_line_arguments.extent[0]),
            float(command_line_arguments.extent[1]),
            float(command_line_arguments.extent[2]),
            float(command_line_arguments.extent[3]))
    except AttributeError:
        print "No extents"
        pass
    return impact_function
Example #12
0
def impact_function_setup(
        command_line_arguments, hazard, exposure, aggregation=None):
    """Sets up an analysis object.

    .. versionadded:: 3.2

    :param command_line_arguments: User inputs.
    :type command_line_arguments: CommandLineArguments

    :param hazard: Hazard layer
    :type hazard: QgsLayer

    :param exposure: Exposure Layer
    :type exposure: QgsLayer

    :param aggregation: Aggregation Layer
    :type aggregation: QgsLayer

    :raises: Exception
    """
    # IF
    impact_function = ImpactFunction()

    impact_function.hazard = hazard
    impact_function.exposure = exposure
    impact_function.aggregation = aggregation
    impact_function.map_canvas = CANVAS
    # QSetting context
    settings = QSettings()
    crs = settings.value('inasafe/user_extent_crs', '', type=str)
    impact_function.requested_extent_crs = QgsCoordinateReferenceSystem(crs)
    try:
        impact_function.requested_extent = QgsRectangle(
            float(command_line_arguments.extent[0]),
            float(command_line_arguments.extent[1]),
            float(command_line_arguments.extent[2]),
            float(command_line_arguments.extent[3])
        )
    except AttributeError:
        print "No extents"
        pass
    return impact_function
    def test_provenance_with_aggregation(self):
        """Test provenance of impact function with aggregation."""
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'building-points.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        hazard = definition(hazard_layer.keywords['hazard'])
        exposure = definition(exposure_layer.keywords['exposure'])
        hazard_category = definition(hazard_layer.keywords['hazard_category'])

        expected_provenance = {
            provenance_gdal_version['provenance_key']:
            gdal.__version__,
            provenance_host_name['provenance_key']:
            gethostname(),
            provenance_map_title['provenance_key']:
            get_map_title(hazard, exposure, hazard_category),
            provenance_map_legend_title['provenance_key']:
            exposure['layer_legend_title'],
            provenance_user['provenance_key']:
            getpass.getuser(),
            provenance_os['provenance_key']:
            readable_os_version(),
            provenance_pyqt_version['provenance_key']:
            PYQT_VERSION_STR,
            provenance_qgis_version['provenance_key']:
            QGis.QGIS_VERSION,
            provenance_qt_version['provenance_key']:
            QT_VERSION_STR,
            provenance_inasafe_version['provenance_key']:
            get_version(),
            provenance_aggregation_layer['provenance_key']:
            aggregation_layer.source(),
            provenance_aggregation_layer_id['provenance_key']:
            aggregation_layer.id(),
            provenance_exposure_layer['provenance_key']:
            exposure_layer.source(),
            provenance_exposure_layer_id['provenance_key']:
            exposure_layer.id(),
            provenance_hazard_layer['provenance_key']:
            hazard_layer.source(),
            provenance_hazard_layer_id['provenance_key']:
            hazard_layer.id(),
            provenance_analysis_question['provenance_key']:
            get_analysis_question(hazard, exposure),
            provenance_aggregation_keywords['provenance_key']:
            deepcopy(aggregation_layer.keywords),
            provenance_exposure_keywords['provenance_key']:
            deepcopy(exposure_layer.keywords),
            provenance_hazard_keywords['provenance_key']:
            deepcopy(hazard_layer.keywords),
        }

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        self.maxDiff = None

        expected_provenance.update({
            provenance_action_checklist['provenance_key']:
            impact_function.action_checklist(),
            provenance_analysis_extent['provenance_key']:
            impact_function.analysis_extent.exportToWkt(),
            provenance_impact_function_name['provenance_key']:
            impact_function.name,
            provenance_impact_function_title['provenance_key']:
            impact_function.title,
            provenance_notes['provenance_key']:
            impact_function.notes(),
            provenance_requested_extent['provenance_key']:
            impact_function.requested_extent,
            provenance_data_store_uri['provenance_key']:
            impact_function.datastore.uri_path,
            provenance_start_datetime['provenance_key']:
            impact_function.start_datetime,
            provenance_end_datetime['provenance_key']:
            impact_function.end_datetime,
            provenance_duration['provenance_key']:
            impact_function.duration
        })

        self.assertDictContainsSubset(expected_provenance,
                                      impact_function.provenance)

        output_layer_provenance_keys = [
            provenance_layer_exposure_summary['provenance_key'],
            provenance_layer_aggregate_hazard_impacted['provenance_key'],
            provenance_layer_aggregation_summary['provenance_key'],
            provenance_layer_analysis_impacted['provenance_key'],
            provenance_layer_exposure_summary_table['provenance_key']
        ]

        for key in output_layer_provenance_keys:
            self.assertIn(key, impact_function.provenance.keys())
    def test_ratios_with_raster_exposure(self):
        """Test if we can add defaults to a raster exposure.

        See ticket #3851 how to manage ratios with a raster exposure.
        """
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'tsunami_vector.geojson')
        exposure_layer = load_test_raster_layer('gisv4', 'exposure', 'raster',
                                                'population.asc')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        for layer in impact_function.outputs:
            if layer.keywords['layer_purpose'] == (
                    layer_purpose_analysis_impacted['key']):
                analysis = layer
            if layer.keywords['layer_purpose'] == (
                    layer_purpose_aggregate_hazard_impacted['key']):
                impact = layer

        # We check in the impact layer if we have :
        # female default ratio with the default value
        index = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        unique_values = impact.uniqueValues(index)
        self.assertEqual(1, len(unique_values))
        female_ratio = unique_values[0]

        # female displaced count and youth displaced count
        self.assertNotEqual(
            -1,
            impact.fieldNameIndex(female_displaced_count_field['field_name']))
        self.assertNotEqual(
            -1,
            impact.fieldNameIndex(youth_displaced_count_field['field_name']))

        # Check that we have more than 0 female displaced in the analysis layer
        index = analysis.fieldNameIndex(
            female_displaced_count_field['field_name'])
        female_displaced = analysis.uniqueValues(index)[0]
        self.assertGreater(female_displaced, 0)

        # Let's check computation
        index = analysis.fieldNameIndex(displaced_field['field_name'])
        displaced_population = analysis.uniqueValues(index)[0]
        self.assertEqual(int(displaced_population * female_ratio),
                         female_displaced)

        # Check that we have more than 0 youth displaced in the analysis layer
        index = analysis.fieldNameIndex(
            female_displaced_count_field['field_name'])
        value = analysis.uniqueValues(index)[0]
        self.assertGreater(value, 0)

        # Let do another test with the special aggregation layer
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'tsunami_vector.geojson')
        exposure_layer = load_test_raster_layer('gisv4', 'exposure', 'raster',
                                                'population.asc')

        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid_ratios.geojson')
        # This aggregation layer has :
        # * a field for female ratio : 1, 0.5 and 0
        # * use global default for youth ratio
        # * do not ust for adult ratio
        # * use custom 0.75 for elderly ratio

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.aggregation = aggregation_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # We should have a female_ratio with many values
        index = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(3, len(values))

        # We should have a youth_ratio with global default
        index = impact.fieldNameIndex(youth_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(1, len(values))

        # We should not have an adult_ratio
        index = impact.fieldNameIndex(adult_ratio_field['field_name'])
        self.assertEqual(-1, index)

        # We should have a elderly_ratio = 0.75
        index = impact.fieldNameIndex(elderly_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(1, len(values))
        self.assertEqual(0.75, values[0])
    def test_ratios_with_vector_exposure(self):
        """Test if we can add defaults to a vector exposure."""
        # First test, if we do not provide an aggregation,
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'population.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.prepare()
        # Let's remove one field from keywords.
        # We monkey patch keywords for testing after `prepare` & before `run`.
        fields = impact_function.exposure.keywords['inasafe_fields']
        del fields[female_count_field['key']]
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # We check the field exist after the IF with only one value.
        field = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(1, len(unique_ratio), unique_ratio)
        self.assertEqual(unique_ratio[0],
                         female_ratio_default_value['default_value'])

        # Second test, if we provide an aggregation without a default ratio 0.2
        expected_ratio = 1.0
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'population.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.debug_mode = True
        impact_function.prepare()
        # The `prepare` reads keywords from the file.
        impact_function.aggregation.keywords['inasafe_default_values'] = {
            elderly_ratio_field['key']: expected_ratio
        }
        fields = impact_function.exposure.keywords['inasafe_fields']
        del fields[female_count_field['key']]
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)
        impact = impact_function.impact

        # We check the field exist after the IF with only original values.
        field = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(3, len(unique_ratio), unique_ratio)

        # We check the field exist after the IF with only one value.
        field = impact.fieldNameIndex(elderly_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(1, len(unique_ratio), unique_ratio)
        self.assertEqual(expected_ratio, unique_ratio[0])

        # Third test, if we provide an aggregation with a ratio and the
        # exposure has a count, we should a have a ratio from the exposure
        # count.
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'population.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.aggregation = aggregation_layer
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # Check that we have don't have only one unique value since the ratio
        # depends on the "population / female count" and we should have at
        # least different ratios.
        field = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertNotEqual(1, len(unique_ratio), unique_ratio)
    def test_minimum_extent(self):
        """Test we can compute the minimum extent in the IF."""
        # Without aggregation layer
        hazard_layer = load_test_vector_layer(
            'hazard', 'flood_multipart_polygons.shp')
        exposure_layer = load_test_vector_layer('exposure', 'roads.shp')
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent without an aggregation layer is '
            'failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon (('
                '106.8080099999999959 -6.19531000000000009, '
                '106.8080099999999959 -6.16752599999999962, '
                '106.83456946836641066 -6.16752599999999962, '
                '106.83456946836641066 -6.19531000000000009, '
                '106.8080099999999959 -6.19531000000000009))',
                impact_function.analysis_extent.exportToWkt()),
            message
        )

        # Without aggregation layer but with a requested_extent
        hazard_layer = load_test_vector_layer(
            'hazard', 'flood_multipart_polygons.shp')
        exposure_layer = load_test_vector_layer('exposure', 'roads.shp')
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.requested_extent = wkt_to_rectangle(
            'POLYGON (('
            '106.772279 -6.237576, '
            '106.772279 -6.165415, '
            '106.885165 -6.165415, '
            '106.885165 -6.237576, '
            '106.772279 -6.237576'
            '))')
        impact_function.requested_extent_crs = QgsCoordinateReferenceSystem(
            4326)

        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent without an aggregation layer but '
            'with a requested extent is failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon (('
                '106.8080099999999959 -6.19531000000000009, '
                '106.8080099999999959 -6.16752599999999962, '
                '106.83456946836641066 -6.16752599999999962, '
                '106.83456946836641066 -6.19531000000000009, '
                '106.8080099999999959 -6.19531000000000009))',
                impact_function.analysis_extent.exportToWkt()),
            message
        )

        # With an aggregation layer, without selection
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'building-points.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.use_selected_features_only = False
        impact_function.aggregation.select(0)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent with an aggregation layer is '
            'failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon ((106.9033179652593617 -6.18324454090033182, '
                '106.90331796525939012 -6.2725478115989306, '
                '106.72365490843547775 -6.2725478115989306, '
                '106.72365490843547775 -6.18324645462287137, '
                '106.72365490843547775 -6.09392810187095257, '
                '106.81348643684744104 -6.09392810187095257, '
                '106.9033179652593617 -6.09392810187095257, '
                '106.9033179652593617 -6.18324454090033182))',
                impact_function.analysis_extent.exportToWkt()),
            message
        )

        # With an aggregation layer, with selection
        impact_function.use_selected_features_only = True
        impact_function.aggregation = aggregation_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent with an aggregation layer and '
            'a selection is failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon ((106.72365490843547775 -6.09392810187095257, '
                '106.81348643684744104 -6.09392810187095257, '
                '106.81348643684744104 -6.18324645462287137, '
                '106.72365490843547775 -6.18324645462287137, '
                '106.72365490843547775 -6.09392810187095257))',
                impact_function.analysis_extent.exportToWkt()),
            message
        )
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
Example #18
0
    def test_provenance_with_aggregation(self):
        """Test provenance of impact function with aggregation."""
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'building-points.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')

        hazard = definition(hazard_layer.keywords['hazard'])
        exposure = definition(exposure_layer.keywords['exposure'])
        hazard_category = definition(hazard_layer.keywords['hazard_category'])

        expected_provenance = {
            'gdal_version': gdal.__version__,
            'host_name': gethostname(),
            'map_title': get_map_title(hazard, exposure, hazard_category),
            'map_legend_title': exposure['layer_legend_title'],
            'user': getpass.getuser(),
            'os': readable_os_version(),
            'pyqt_version': PYQT_VERSION_STR,
            'qgis_version': QGis.QGIS_VERSION,
            'qt_version': QT_VERSION_STR,
            'inasafe_version': get_version(),
            'aggregation_layer': aggregation_layer.source(),
            'aggregation_layer_id': aggregation_layer.id(),
            'exposure_layer': exposure_layer.source(),
            'exposure_layer_id': exposure_layer.id(),
            'hazard_layer': hazard_layer.source(),
            'hazard_layer_id': hazard_layer.id(),
            'analysis_question': get_analysis_question(hazard, exposure),
            'aggregation_keywords': deepcopy(aggregation_layer.keywords),
            'exposure_keywords': deepcopy(exposure_layer.keywords),
            'hazard_keywords': deepcopy(hazard_layer.keywords),
        }

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        self.maxDiff = None

        expected_provenance.update({
            'action_checklist':
            impact_function.action_checklist(),
            'analysis_extent':
            impact_function.analysis_extent.exportToWkt(),
            'impact_function_name':
            impact_function.name,
            'impact_function_title':
            impact_function.title,
            'notes':
            impact_function.notes(),
            'requested_extent':
            impact_function.requested_extent,
            'data_store_uri':
            impact_function.datastore.uri_path,
            'start_datetime':
            impact_function.start_datetime,
            'end_datetime':
            impact_function.end_datetime,
            'duration':
            impact_function.duration
        })

        self.assertDictEqual(expected_provenance, impact_function.provenance)
    def test_provenance_with_aggregation(self):
        """Test provenance of impact function with aggregation."""
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'building-points.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        hazard = definition(hazard_layer.keywords['hazard'])
        exposure = definition(exposure_layer.keywords['exposure'])
        hazard_category = definition(hazard_layer.keywords['hazard_category'])

        expected_provenance = {
            'gdal_version': gdal.__version__,
            'host_name': gethostname(),
            'map_title': get_map_title(hazard, exposure, hazard_category),
            'map_legend_title': exposure['layer_legend_title'],
            'user': getpass.getuser(),
            'os': readable_os_version(),
            'pyqt_version': PYQT_VERSION_STR,
            'qgis_version': QGis.QGIS_VERSION,
            'qt_version': QT_VERSION_STR,
            'inasafe_version': get_version(),
            'aggregation_layer': aggregation_layer.source(),
            'aggregation_layer_id': aggregation_layer.id(),
            'exposure_layer': exposure_layer.source(),
            'exposure_layer_id': exposure_layer.id(),
            'hazard_layer': hazard_layer.source(),
            'hazard_layer_id': hazard_layer.id(),
            'analysis_question': get_analysis_question(hazard, exposure),
            'aggregation_keywords': deepcopy(aggregation_layer.keywords),
            'exposure_keywords': deepcopy(exposure_layer.keywords),
            'hazard_keywords': deepcopy(hazard_layer.keywords),
        }

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        self.maxDiff = None

        expected_provenance.update({
            'action_checklist': impact_function.action_checklist(),
            'analysis_extent': impact_function.analysis_extent.exportToWkt(),
            'impact_function_name': impact_function.name,
            'impact_function_title': impact_function.title,
            'notes': impact_function.notes(),
            'requested_extent': impact_function.requested_extent,
            'data_store_uri': impact_function.datastore.uri_path,
            'start_datetime': impact_function.start_datetime,
            'end_datetime': impact_function.end_datetime,
            'duration': impact_function.duration
        })

        self.assertDictEqual(expected_provenance, impact_function.provenance)
    def test_provenance_with_aggregation(self):
        """Test provenance of impact function with aggregation."""
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'building-points.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        hazard = definition(hazard_layer.keywords['hazard'])
        exposure = definition(exposure_layer.keywords['exposure'])
        hazard_category = definition(hazard_layer.keywords['hazard_category'])

        expected_provenance = {
            provenance_gdal_version['provenance_key']: gdal.__version__,
            provenance_host_name['provenance_key']: gethostname(),
            provenance_map_title['provenance_key']: get_map_title(
                hazard, exposure, hazard_category),
            provenance_map_legend_title['provenance_key']: exposure[
                'layer_legend_title'],
            provenance_user['provenance_key']: getpass.getuser(),
            provenance_os['provenance_key']: readable_os_version(),
            provenance_pyqt_version['provenance_key']: PYQT_VERSION_STR,
            provenance_qgis_version['provenance_key']: QGis.QGIS_VERSION,
            provenance_qt_version['provenance_key']: QT_VERSION_STR,
            provenance_inasafe_version['provenance_key']: get_version(),
            provenance_aggregation_layer['provenance_key']:
                aggregation_layer.source(),
            provenance_aggregation_layer_id['provenance_key']:
                aggregation_layer.id(),
            provenance_exposure_layer['provenance_key']:
                exposure_layer.source(),
            provenance_exposure_layer_id['provenance_key']:
                exposure_layer.id(),
            provenance_hazard_layer['provenance_key']: hazard_layer.source(),
            provenance_hazard_layer_id['provenance_key']: hazard_layer.id(),
            provenance_analysis_question['provenance_key']:
                get_analysis_question(hazard, exposure),
            provenance_aggregation_keywords['provenance_key']: deepcopy(
                aggregation_layer.keywords),
            provenance_exposure_keywords['provenance_key']:
                deepcopy(exposure_layer.keywords),
            provenance_hazard_keywords['provenance_key']: deepcopy(
                hazard_layer.keywords),
        }

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        self.assertDictEqual({}, impact_function.provenance)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        self.maxDiff = None

        expected_provenance.update({
            provenance_action_checklist['provenance_key']:
                impact_function.action_checklist(),
            provenance_analysis_extent['provenance_key']:
                impact_function.analysis_extent.exportToWkt(),
            provenance_impact_function_name['provenance_key']:
                impact_function.name,
            provenance_impact_function_title['provenance_key']:
                impact_function.title,
            provenance_notes['provenance_key']: impact_function.notes(),
            provenance_requested_extent['provenance_key']: impact_function.
                requested_extent,
            provenance_data_store_uri['provenance_key']: impact_function.
                datastore.uri_path,
            provenance_start_datetime['provenance_key']: impact_function.
                start_datetime,
            provenance_end_datetime['provenance_key']:
                impact_function.end_datetime,
            provenance_duration['provenance_key']: impact_function.duration
        })

        self.assertDictContainsSubset(
            expected_provenance, impact_function.provenance)

        output_layer_provenance_keys = [
            provenance_layer_exposure_summary['provenance_key'],
            provenance_layer_aggregate_hazard_impacted['provenance_key'],
            provenance_layer_aggregation_summary['provenance_key'],
            provenance_layer_analysis_impacted['provenance_key'],
            provenance_layer_exposure_summary_table['provenance_key']
        ]

        for key in output_layer_provenance_keys:
            self.assertIn(key, impact_function.provenance.keys())
    def test_ratios_with_raster_exposure(self):
        """Test if we can add defaults to a raster exposure.

        See ticket #3851 how to manage ratios with a raster exposure.
        """
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'tsunami_vector.geojson')
        exposure_layer = load_test_raster_layer(
            'gisv4', 'exposure', 'raster', 'population.asc')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        for layer in impact_function.outputs:
            if layer.keywords['layer_purpose'] == (
                    layer_purpose_analysis_impacted['key']):
                analysis = layer
            if layer.keywords['layer_purpose'] == (
                    layer_purpose_aggregate_hazard_impacted['key']):
                impact = layer

        # We check in the impact layer if we have :
        # female default ratio with the default value
        index = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        unique_values = impact.uniqueValues(index)
        self.assertEqual(1, len(unique_values))
        female_ratio = unique_values[0]

        # female displaced count and youth displaced count
        self.assertNotEqual(
            -1, impact.fieldNameIndex(
                female_displaced_count_field['field_name']))
        self.assertNotEqual(
            -1, impact.fieldNameIndex(
                youth_displaced_count_field['field_name']))

        # Check that we have more than 0 female displaced in the analysis layer
        index = analysis.fieldNameIndex(
            female_displaced_count_field['field_name'])
        female_displaced = analysis.uniqueValues(index)[0]
        self.assertGreater(female_displaced, 0)

        # Let's check computation
        index = analysis.fieldNameIndex(
            displaced_field['field_name'])
        displaced_population = analysis.uniqueValues(index)[0]
        self.assertEqual(
            int(displaced_population * female_ratio), female_displaced)

        # Check that we have more than 0 youth displaced in the analysis layer
        index = analysis.fieldNameIndex(
            female_displaced_count_field['field_name'])
        value = analysis.uniqueValues(index)[0]
        self.assertGreater(value, 0)

        # Let do another test with the special aggregation layer
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'tsunami_vector.geojson')
        exposure_layer = load_test_raster_layer(
            'gisv4', 'exposure', 'raster', 'population.asc')

        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid_ratios.geojson')
        # This aggregation layer has :
        # * a field for female ratio : 1, 0.5 and 0
        # * use global default for youth ratio
        # * do not ust for adult ratio
        # * use custom 0.75 for elderly ratio

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.aggregation = aggregation_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # We should have a female_ratio with many values
        index = impact.fieldNameIndex(female_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(3, len(values))

        # We should have a youth_ratio with global default
        index = impact.fieldNameIndex(youth_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(1, len(values))

        # We should not have an adult_ratio
        index = impact.fieldNameIndex(adult_ratio_field['field_name'])
        self.assertEqual(-1, index)

        # We should have a elderly_ratio = 0.75
        index = impact.fieldNameIndex(elderly_ratio_field['field_name'])
        self.assertNotEqual(-1, index)
        values = impact.uniqueValues(index)
        self.assertEqual(1, len(values))
        self.assertEqual(0.75, values[0])
    def test_ratios_with_vector_exposure(self):
        """Test if we can add defaults to a vector exposure."""
        # First test, if we do not provide an aggregation,
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'population.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.prepare()
        # Let's remove one field from keywords.
        # We monkey patch keywords for testing after `prepare` & before `run`.
        fields = impact_function.exposure.keywords['inasafe_fields']
        del fields[female_count_field['key']]
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # We check the field exist after the IF with only one value.
        field = impact.fieldNameIndex(
            female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(1, len(unique_ratio), unique_ratio)
        self.assertEqual(
            unique_ratio[0], female_ratio_default_value['default_value'])

        # Second test, if we provide an aggregation without a default ratio 0.2
        expected_ratio = 1.0
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'population.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.debug_mode = True
        impact_function.prepare()
        # The `prepare` reads keywords from the file.
        impact_function.aggregation.keywords['inasafe_default_values'] = {
            elderly_ratio_field['key']: expected_ratio
        }
        fields = impact_function.exposure.keywords['inasafe_fields']
        del fields[female_count_field['key']]
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)
        impact = impact_function.impact

        # We check the field exist after the IF with only original values.
        field = impact.fieldNameIndex(
            female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(3, len(unique_ratio), unique_ratio)

        # We check the field exist after the IF with only one value.
        field = impact.fieldNameIndex(
            elderly_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertEqual(1, len(unique_ratio), unique_ratio)
        self.assertEqual(expected_ratio, unique_ratio[0])

        # Third test, if we provide an aggregation with a ratio and the
        # exposure has a count, we should a have a ratio from the exposure
        # count.
        hazard_layer = load_test_vector_layer(
            'gisv4', 'hazard', 'classified_vector.geojson')
        exposure_layer = load_test_vector_layer(
            'gisv4', 'exposure', 'population.geojson')
        aggregation_layer = load_test_vector_layer(
            'gisv4', 'aggregation', 'small_grid.geojson')

        # Set up impact function
        impact_function = ImpactFunction()
        impact_function.debug_mode = True
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.aggregation = aggregation_layer
        impact_function.prepare()
        status, message = impact_function.run()
        self.assertEqual(ANALYSIS_SUCCESS, status, message)

        impact = impact_function.impact

        # Check that we have don't have only one unique value since the ratio
        # depends on the "population / female count" and we should have at
        # least different ratios.
        field = impact.fieldNameIndex(
            female_ratio_field['field_name'])
        self.assertNotEqual(-1, field)
        unique_ratio = impact.uniqueValues(field)
        self.assertNotEqual(1, len(unique_ratio), unique_ratio)
Example #23
0
    def run_task(self, task_item, status_item, count=0, index=''):
        """Run a single task.

        :param task_item: Table task_item containing task name / details.
        :type task_item: QTableWidgetItem

        :param status_item: Table task_item that holds the task status.
        :type status_item: QTableWidgetItem

        :param count: Count of scenarios that have been run already.
        :type count:

        :param index: The index for the table item that will be run.
        :type index: int

        :returns: Flag indicating if the task succeeded or not.
        :rtype: bool
        """
        self.enable_busy_cursor()
        for layer_group in self.layer_group_container:
            layer_group.setItemVisibilityChecked(False)

        # set status to 'running'
        status_item.setText(self.tr('Running'))

        # .. see also:: :func:`appendRow` to understand the next 2 lines
        variant = task_item.data(QtCore.Qt.UserRole)
        value = variant[0]
        result = True

        if isinstance(value, str):
            filename = value
            # run script
            try:
                self.run_script(filename)
                # set status to 'OK'
                status_item.setText(self.tr('Script OK'))
            except Exception as e:  # pylint: disable=W0703
                # set status to 'fail'
                status_item.setText(self.tr('Script Fail'))

                LOGGER.exception(
                    'Running macro failed. The exception: ' + str(e))
                result = False
        elif isinstance(value, dict):
            # start in new project if toggle is active
            if self.start_in_new_project:
                self.iface.newProject()
            # create layer group
            group_name = value['scenario_name']
            self.layer_group = self.root.addGroup(group_name)
            self.layer_group_container.append(self.layer_group)

            # Its a dict containing files for a scenario
            success, parameters = self.prepare_task(value)
            if not success:
                # set status to 'running'
                status_item.setText(self.tr('Please update scenario'))
                self.disable_busy_cursor()
                return False

            directory = self.output_directory.text()
            if self.scenario_directory_radio.isChecked():
                directory = self.source_directory.text()

            output_directory = os.path.join(directory, group_name)
            if not os.path.exists(output_directory):
                os.makedirs(output_directory)

            # If impact function parameters loaded successfully, initiate IF.
            impact_function = ImpactFunction()
            impact_function.datastore = Folder(output_directory)
            impact_function.datastore.default_vector_format = "geojson"
            impact_function.hazard = parameters[layer_purpose_hazard['key']]
            impact_function.exposure = (
                parameters[layer_purpose_exposure['key']])
            if parameters[layer_purpose_aggregation['key']]:
                impact_function.aggregation = (
                    parameters[layer_purpose_aggregation['key']])
            elif parameters['extent']:
                impact_function.requested_extent = parameters['extent']
                impact_function.crs = parameters['crs']
            prepare_status, prepare_message = impact_function.prepare()
            if prepare_status == PREPARE_SUCCESS:
                LOGGER.info('Impact function ready')
                status, message = impact_function.run()
                if status == ANALYSIS_SUCCESS:
                    status_item.setText(self.tr('Analysis Success'))
                    impact_layer = impact_function.impact
                    if impact_layer.isValid():
                        layer_list = [
                            impact_layer,
                            impact_function.analysis_impacted,
                            parameters[layer_purpose_hazard['key']],
                            parameters[layer_purpose_exposure['key']],
                            parameters[layer_purpose_aggregation['key']]]
                        QgsProject.instance().addMapLayers(
                            layer_list, False)
                        for layer in layer_list:
                            self.layer_group.addLayer(layer)
                        map_canvas = QgsProject.instance().mapLayers()
                        for layer in map_canvas:
                            # turn of layer visibility if not impact layer
                            if map_canvas[layer].id() == impact_layer.id():
                                self.set_layer_visible(
                                    map_canvas[layer], True)
                            else:
                                self.set_layer_visible(
                                    map_canvas[layer], False)

                        # we need to set analysis_impacted as an active layer
                        # because we need to get all qgis variables that we
                        # need from this layer for infographic.
                        if self.iface:
                            self.iface.setActiveLayer(
                                impact_function.analysis_impacted)

                        report_directory = os.path.join(
                            output_directory, 'output')

                        # generate map report and impact report
                        try:
                            error_code, message = (
                                impact_function.generate_report(
                                    all_default_report_components,
                                    report_directory))

                        except BaseException:
                            status_item.setText(
                                self.tr('Report failed to generate.'))
                    else:
                        LOGGER.info('Impact layer is invalid')

                elif status == ANALYSIS_FAILED_BAD_INPUT:
                    LOGGER.info('Bad input detected')

                elif status == ANALYSIS_FAILED_BAD_CODE:
                    LOGGER.info(
                        'Impact function encountered a bug: %s' % message)

            else:
                LOGGER.warning('Impact function not ready')
                send_error_message(self, prepare_message)

        else:
            LOGGER.exception('Data type not supported: "%s"' % value)
            result = False

        self.disable_busy_cursor()
        return result
    def test_minimum_extent(self):
        """Test we can compute the minimum extent in the IF."""
        # Without aggregation layer
        hazard_layer = load_test_vector_layer('hazard',
                                              'flood_multipart_polygons.shp')
        exposure_layer = load_test_vector_layer('exposure', 'roads.shp')
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent without an aggregation layer is '
            'failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon (('
                '106.8080099999999959 -6.19531000000000009, '
                '106.8080099999999959 -6.16752599999999962, '
                '106.83456946836641066 -6.16752599999999962, '
                '106.83456946836641066 -6.19531000000000009, '
                '106.8080099999999959 -6.19531000000000009))',
                impact_function.analysis_extent.exportToWkt()), message)

        # Without aggregation layer but with a requested_extent
        hazard_layer = load_test_vector_layer('hazard',
                                              'flood_multipart_polygons.shp')
        exposure_layer = load_test_vector_layer('exposure', 'roads.shp')
        impact_function = ImpactFunction()
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.requested_extent = wkt_to_rectangle(
            'POLYGON (('
            '106.772279 -6.237576, '
            '106.772279 -6.165415, '
            '106.885165 -6.165415, '
            '106.885165 -6.237576, '
            '106.772279 -6.237576'
            '))')
        impact_function.requested_extent_crs = QgsCoordinateReferenceSystem(
            4326)

        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent without an aggregation layer but '
            'with a requested extent is failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon (('
                '106.8080099999999959 -6.19531000000000009, '
                '106.8080099999999959 -6.16752599999999962, '
                '106.83456946836641066 -6.16752599999999962, '
                '106.83456946836641066 -6.19531000000000009, '
                '106.8080099999999959 -6.19531000000000009))',
                impact_function.analysis_extent.exportToWkt()), message)

        # With an aggregation layer, without selection
        hazard_layer = load_test_vector_layer('gisv4', 'hazard',
                                              'classified_vector.geojson')
        exposure_layer = load_test_vector_layer('gisv4', 'exposure',
                                                'building-points.geojson')
        aggregation_layer = load_test_vector_layer('gisv4', 'aggregation',
                                                   'small_grid.geojson')
        impact_function = ImpactFunction()
        impact_function.aggregation = aggregation_layer
        impact_function.exposure = exposure_layer
        impact_function.hazard = hazard_layer
        impact_function.use_selected_features_only = False
        impact_function.aggregation.select(0)
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent with an aggregation layer is '
            'failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon ((106.9033179652593617 -6.18324454090033182, '
                '106.90331796525939012 -6.2725478115989306, '
                '106.72365490843547775 -6.2725478115989306, '
                '106.72365490843547775 -6.18324645462287137, '
                '106.72365490843547775 -6.09392810187095257, '
                '106.81348643684744104 -6.09392810187095257, '
                '106.9033179652593617 -6.09392810187095257, '
                '106.9033179652593617 -6.18324454090033182))',
                impact_function.analysis_extent.exportToWkt()), message)

        # With an aggregation layer, with selection
        impact_function.use_selected_features_only = True
        impact_function.aggregation = aggregation_layer
        status, message = impact_function.prepare()
        self.assertEqual(PREPARE_SUCCESS, status, message)
        message = (
            'Test about the minimum extent with an aggregation layer and '
            'a selection is failing.')
        self.assertTrue(
            compare_wkt(
                'Polygon ((106.72365490843547775 -6.09392810187095257, '
                '106.81348643684744104 -6.09392810187095257, '
                '106.81348643684744104 -6.18324645462287137, '
                '106.72365490843547775 -6.18324645462287137, '
                '106.72365490843547775 -6.09392810187095257))',
                impact_function.analysis_extent.exportToWkt()), message)
Example #25
0
    def run_task(self, task_item, status_item, count=0, index=''):
        """Run a single task.

        :param task_item: Table task_item containing task name / details.
        :type task_item: QTableWidgetItem

        :param status_item: Table task_item that holds the task status.
        :type status_item: QTableWidgetItem

        :param count: Count of scenarios that have been run already.
        :type count:

        :param index: The index for the table item that will be run.
        :type index: int

        :returns: Flag indicating if the task succeeded or not.
        :rtype: bool
        """
        self.enable_busy_cursor()
        for layer_group in self.layer_group_container:
            layer_group.setVisible(False)

        # set status to 'running'
        status_item.setText(self.tr('Running'))

        # .. see also:: :func:`appendRow` to understand the next 2 lines
        variant = task_item.data(QtCore.Qt.UserRole)
        value = variant[0]
        result = True

        if isinstance(value, str):
            filename = value
            # run script
            try:
                self.run_script(filename)
                # set status to 'OK'
                status_item.setText(self.tr('Script OK'))
            except Exception as e:  # pylint: disable=W0703
                # set status to 'fail'
                status_item.setText(self.tr('Script Fail'))

                LOGGER.exception('Running macro failed. The exception: ' +
                    str(e))
                result = False
        elif isinstance(value, dict):
            # start in new project if toggle is active
            if self.start_in_new_project:
                self.iface.newProject()
            # create layer group
            group_name = value['scenario_name']
            self.layer_group = self.root.addGroup(group_name)
            self.layer_group_container.append(self.layer_group)

            # Its a dict containing files for a scenario
            success, parameters = self.prepare_task(value)
            if not success:
                # set status to 'running'
                status_item.setText(self.tr('Please update scenario'))
                self.disable_busy_cursor()
                return False
            # If impact function parameters loaded successfully, initiate IF.
            impact_function = ImpactFunction()
            impact_function.hazard = parameters[layer_purpose_hazard['key']]
            impact_function.exposure = (
                parameters[layer_purpose_exposure['key']])
            if parameters[layer_purpose_aggregation['key']]:
                impact_function.aggregation = (
                    parameters[layer_purpose_aggregation['key']])
            elif parameters['extent']:
                impact_function.requested_extent = parameters['extent']
                impact_function.requested_extent_crs = parameters['crs']
            prepare_status, prepare_message = impact_function.prepare()
            if prepare_status == PREPARE_SUCCESS:
                LOGGER.info('Impact function ready')
                status, message = impact_function.run()
                if status == ANALYSIS_SUCCESS:
                    status_item.setText(self.tr('Analysis Success'))
                    impact_layer = impact_function.impact
                    if impact_layer.isValid():
                        layer_list = [
                            impact_layer,
                            parameters[layer_purpose_hazard['key']],
                            parameters[layer_purpose_exposure['key']],
                            parameters[layer_purpose_aggregation['key']]]
                        QgsMapLayerRegistry.instance().addMapLayers(
                            layer_list, False)
                        for layer in layer_list:
                            self.layer_group.addLayer(layer)
                        map_canvas = QgsMapLayerRegistry.instance().mapLayers()
                        for layer in map_canvas:
                            # turn of layer visibility if not impact layer
                            if map_canvas[layer].id() == impact_layer.id():
                                self.legend.setLayerVisible(
                                    map_canvas[layer], True)
                            else:
                                self.legend.setLayerVisible(
                                    map_canvas[layer], False)

                        # generate map report and impact report
                        try:
                            # this line is to save the impact report in default
                            # InaSAFE directory.
                            generate_impact_report(impact_function, self.iface)
                            generate_impact_map_report(
                                impact_function,
                                self.iface)
                            # this line is to save the report in user specified
                            # directory.
                            self.generate_pdf_report(
                                impact_function,
                                self.iface,
                                group_name)
                        except:
                            status_item.setText(
                                self.tr('Report failed to generate.'))
                    else:
                        LOGGER.info('Impact layer is invalid')

                elif status == ANALYSIS_FAILED_BAD_INPUT:
                    LOGGER.info('Bad input detected')

                elif status == ANALYSIS_FAILED_BAD_CODE:
                    LOGGER.info('Impact function encountered a bug')

            else:
                LOGGER.warning('Impact function not ready')
                send_error_message(self, prepare_message)

        else:
            LOGGER.exception('Data type not supported: "%s"' % value)
            result = False

        self.disable_busy_cursor()
        return result
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