예제 #1
0
    def show_info(self):
        """Show usage text to the user."""
        header = html_header()
        footer = html_footer()
        string = header

        heading = m.Heading(self.tr('Shakemap Grid Importer'), **INFO_STYLE)
        body = self.tr(
            'This tool will convert an earthquake \'shakemap\' that is in '
            'grid xml format to a GeoTIFF file. The imported file can be used '
            'in InaSAFE as an input for impact functions that require and '
            'earthquake layer.  To use this tool effectively:')
        tips = m.BulletedList()
        tips.add(self.tr('Select a grid.xml for the input layer.'))
        tips.add(self.tr('Choose where to write the output layer to.'))
        tips.add(
            self.
            tr('Choose the interpolation algorithm that should be used when '
               'converting the xml grid to a raster. If unsure keep the default.'
               ))
        tips.add(
            self.
            tr('If you want to obtain shake data you can get it for free from '
               'the USGS shakemap site: '
               'http://earthquake.usgs.gov/earthquakes/shakemap/list.php?y=2013'
               ))
        message = m.Message()
        message.add(heading)
        message.add(body)
        message.add(tips)
        string += message.to_html()
        string += footer

        self.webView.setHtml(string)
예제 #2
0
    def show_info(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        header = html_header()
        footer = html_footer()

        string = header

        heading = m.Heading(self.tr('User Extents Tool'), **INFO_STYLE)
        body = self.tr(
            'This tool allows you to specify exactly which geographical '
            'region should be used for your analysis. You can either '
            'enter the coordinates directly into the input boxes below '
            '(using the same CRS as the canvas is currently set to), or '
            'you can interactively select the area by using the \'select '
            'on map\' button - which will temporarily hide this window and '
            'allow you to drag a rectangle on the map. After you have '
            'finished dragging the rectangle, this window will reappear. '
            'If you enable the \'Toggle scenario outlines\' tool on the '
            'InaSAFE toolbar, your user defined extent will be shown on '
            'the map as a blue rectangle. Please note that when running '
            'your analysis, the effective analysis extent will be the '
            'intersection of the hazard extent, exposure extent and user '
            'extent - thus the entire user extent area may not be used for '
            'analysis.')

        message = m.Message()
        message.add(heading)
        message.add(body)
        string += message.to_html()
        string += footer

        self.web_view.setHtml(string)
예제 #3
0
    def get_output(self, aoi_mode):
        """Returns the results of the post processing as a table.

        :param aoi_mode: aoi mode of the aggregator.
        :type aoi_mode: bool

        :returns: str - a string containing the html in the requested format.
        """
        if self.error_message is not None:
            message = m.Message(
                m.Heading(self.tr('Postprocessing report skipped')),
                m.Paragraph(
                    self.tr(
                        'Due to a problem while processing the results,'
                        ' the detailed postprocessing report is unavailable:'
                        ' %s') % self.error_message))
            return message
        else:
            try:
                if (self.keyword_io.read_keywords(self.aggregator.layer,
                                                  'had multipart polygon')):
                    self._consolidate_multipart_stats()
            except KeywordNotFoundError:
                pass

            return self._generate_tables(aoi_mode)
예제 #4
0
 def Xtest_static_and_error(self):
     """Test error message works when there is a static message in place."""
     self.message_viewer.static_message_event(None, m.Message('Hi'))
     text = self.fake_error()
     myExpectedResult = open(
         TEST_FILES_DIR + '/test-static-error-message.txt',
         'r').read().replace('\n', '')
     self.assertEqual(text, myExpectedResult)
예제 #5
0
    def show_info(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        header = html_header()
        footer = html_footer()

        string = header

        heading = m.Heading(self.tr('Impact Layer Merge Tool'), **INFO_STYLE)
        body = self.tr(
            'This tool will merge the outputs from two impact maps for the '
            'same area. The maps must be created using the same aggregation '
            'areas and same hazard. To use:')
        tips = m.BulletedList()
        tips.add(
            self.tr(
                'Run an impact assessment for an area using aggregation. e.g.'
                'Flood Impact on Buildings aggregated by municipal boundaries.'
            ))
        tips.add(
            self.
            tr('Run a second impact assessment for the same area using the same '
               'aggregation. e.g. Flood Impact on People aggregated by '
               'municipal boundaries.'))
        tips.add(
            self.
            tr('Open this tool and select each impact layer from the pick lists '
               'provided below.'))
        tips.add(
            self.tr(
                'Select the aggregation layer that was used to generate the '
                'first and second impact layer.'))
        tips.add(self.tr('Select an output directory.'))
        tips.add(
            self.
            tr('Check "Use customized report template" checkbox and select the '
               'report template file if you want to use your own template. Note '
               'that all the map composer components that are needed must be '
               'fulfilled.'))
        tips.add(
            self.tr('Click OK to generate the per aggregation area combined '
                    'summaries.'))
        message = m.Message()
        message.add(heading)
        message.add(body)
        message.add(tips)
        string += message.to_html()
        string += footer

        self.web_view.setHtml(string)
예제 #6
0
    def show_info(self):
        """Show usage info to the user."""
        # Read the header and footer html snippets
        header = html_header()
        footer = html_footer()

        string = header

        heading = m.Heading(self.tr('OSM Downloader'), **INFO_STYLE)
        body = self.tr(
            'This tool will fetch building (\'structure\') data from the '
            'OpenStreetMap project for you. The downloaded data will have '
            'InaSAFE keywords defined and a default QGIS style applied. To '
            'use this tool effectively:')
        tips = m.BulletedList()
        tips.add(
            self.
            tr('Use QGIS to zoom in to the area for which you want building data '
               'to be retrieved.'))
        tips.add(
            self.
            tr('Check the output directory is correct. Note that the saved '
               'dataset will be called buildings.shp (and its associated files).'
               ))
        tips.add(
            self.
            tr('If a dataset already exists in the output directory it will be '
               'overwritten.'))
        tips.add(
            self.
            tr('This tool requires a working internet connection and fetching '
               'buildings will consume your bandwidth.'))
        tips.add(
            m.Link(
                'http://www.openstreetmap.org/copyright',
                text=self.tr(
                    'Downloaded data is copyright OpenStreetMap contributors'
                    ' (click for more info).')))
        message = m.Message()
        message.add(heading)
        message.add(body)
        message.add(tips)
        string += message.to_html()
        string += footer

        self.webView.setHtml(string)
예제 #7
0
def get_error_message(exception, context=None, suggestion=None):
    """Convert exception into an ErrorMessage containing a stack trace.


    :param exception: Exception object.
    :type exception: Exception

    :param context: Optional context message.
    :type context: str

    :param suggestion: Optional suggestion.
    :type suggestion: str

    .. see also:: https://github.com/AIFDR/inasafe/issues/577

    :returns: An error message with stack trace info suitable for display.
    :rtype: ErrorMessage
    """

    myTraceback = ''.join(traceback.format_tb(sys.exc_info()[2]))

    myProblem = m.Message(m.Text(exception.__class__.__name__))

    if str(exception) is None or str(exception) == '':
        myProblem.append = m.Text(tr('No details provided'))
    else:
        myProblem.append = m.Text(str(exception))

    mySuggestion = suggestion
    if mySuggestion is None and hasattr(exception, 'suggestion'):
        mySuggestion = exception.message

    myErrorMessage = ErrorMessage(
        myProblem,
        detail=context,
        suggestion=mySuggestion,
        traceback=myTraceback
    )

    myArgs = exception.args
    for myArg in myArgs:
        myErrorMessage.details.append(myArg)

    return myErrorMessage
예제 #8
0
    def update_warning(self):
        """Update warning message and enable/disable Ok button."""
        if len(self.warning_text) == 0:
            self.button_box.button(QDialogButtonBox.Ok).setEnabled(True)
            return

        header = html_header()
        footer = html_footer()
        string = header
        heading = m.Heading(self.tr('Shakemap Grid Importer'), **INFO_STYLE)
        tips = m.BulletedList()
        self.button_box.button(QDialogButtonBox.Ok).setEnabled(False)
        message = m.Message()
        message.add(heading)
        for warning in self.warning_text:
            tips.add(warning)

        message.add(tips)
        string += message.to_html()
        string += footer
        self.webView.setHtml(string)
예제 #9
0
    def getOutput(self):
        """Returns the results of the post processing as a table.

        Args:
            theSingleTableFlag - bool indicating if result should be rendered
                as a single table. Default False.

        Returns: str - a string containing the html in the requested format.
        """

        # LOGGER.debug(self.postProcessingOutput)
        if self.errorMessage is not None:
            myMessage = m.Message(
                m.Heading(self.tr('Postprocessing report skipped')),
                m.Paragraph(
                    self.tr(
                        'Due to a problem while processing the results,'
                        ' the detailed postprocessing report is unavailable:'
                        ' %1').arg(self.errorMessage)))
            return myMessage

        return self._generateTables()
    def show_info(self):
        """Show basic usage instructions."""
        header = html_header()
        footer = html_footer()
        string = header

        heading = m.Heading(self.tr('Minimum Needs Calculator'), **INFO_STYLE)
        body = self.tr(
            'This tool will calculated minimum needs for evacuated people. To '
            'use this tool effectively:')
        tips = m.BulletedList()
        tips.add(
            self.
            tr('Load a polygon layer in QGIS. Typically the layer will '
               'represent administrative districts where people have gone to an '
               'evacuation center.'))
        tips.add(
            self.
            tr('Ensure that the layer has an INTEGER attribute for the number of '
               'displaced people associated with each feature.'))
        tips.add(
            self.
            tr('Use the pick lists below to select the layer and the population '
               'field and then press \'OK\'.'))
        tips.add(
            self.
            tr('A new layer will be added to QGIS after the calculation is '
               'complete. The layer will contain the minimum needs per district '
               '/ administrative boundary.'))
        message = m.Message()
        message.add(heading)
        message.add(body)
        message.add(tips)
        string += message.to_html()
        string += footer

        self.webView.setHtml(string)
예제 #11
0
    def _generate_tables(self):
        """Parses the postprocessing output as one table per postprocessor.

        TODO: This should rather return json and then have a helper method to
        make html from the JSON.

        :returns: The html.
        :rtype: str
        """
        message = m.Message()

        for processor, results_list in self.output.iteritems():

            self.current_output_postprocessor = processor
            # results_list is for example:
            # [
            #    (PyQt4.QtCore.QString(u'Entire area'), OrderedDict([
            #        (u'Total', {'value': 977536, 'metadata': {}}),
            #        (u'Female population', {'value': 508319, 'metadata': {}}),
            #        (u'Weekly hygiene packs', {'value': 403453, 'metadata': {
            #         'description': 'Females hygiene packs for weekly use'}})
            #    ]))
            #]

            #sorting using the first indicator of a postprocessor
            sorted_results = sorted(
                results_list,
                key=self._sort_no_data,
                reverse=True)

            #init table
            has_no_data = False
            table = m.Table(
                style_class='table table-condensed table-striped')
            table.caption = self.tr('Detailed %s report') % (safeTr(
                get_postprocessor_human_name(processor)).lower())

            header = m.Row()
            header.add(str(self.attribute_title).capitalize())
            for calculation_name in sorted_results[0][1]:
                header.add(self.tr(calculation_name))
            table.add(header)

            # used to calculate the totals row as per issue #690
            postprocessor_totals = OrderedDict()

            for zone_name, calc in sorted_results:
                row = m.Row(zone_name)

                for indicator, calculation_data in calc.iteritems():
                    value = calculation_data['value']
                    if value == self.aggregator.defaults['NO_DATA']:
                        has_no_data = True
                        value += ' *'
                        try:
                            postprocessor_totals[indicator] += 0
                        except KeyError:
                            postprocessor_totals[indicator] = 0
                    else:
                        try:
                            postprocessor_totals[indicator] += int(value)
                        except KeyError:
                            postprocessor_totals[indicator] = int(value)
                    row.add(value)
                table.add(row)

            # add the totals row
            row = m.Row(self.tr('Total in aggregation areas'))
            for _, total in postprocessor_totals.iteritems():
                row.add(str(total))
            table.add(row)

            # add table to message
            message.add(table)
            if has_no_data:
                message.add(m.EmphasizedText(self.tr(
                    '* "%s" values mean that there where some problems while '
                    'calculating them. This did not affect the other '
                    'values.') % (self.aggregator.defaults['NO_DATA'])))

        return message
예제 #12
0
def check_memory_usage(buffered_geo_extent, cell_size):
    """Helper to check if analysis is feasible when extents change.

    For simplicity, we will do all our calculations in geocrs.

    :param buffered_geo_extent: An extent in the for [xmin, ymin, xmax, ymax]
    :type buffered_geo_extent: list

    :param cell_size: The size of a cell (assumes in the X direction).
    :type cell_size: float

    :returns: True if it appears we have enough memory (or we can't compute
        it), False if it appears we do not have enough.
    :rtype: bool

    :raises: A Message containing notes about how much memory is needed
        for a single raster and if this is likely to result in an error.

    :returns: True if it is supposed that there is sufficient memory,
        False if it is supposed that too little memory exists.
    :rtype: bool
    """
    message = m.Message()
    check_heading = m.Heading(tr('Checking available memory'),
                              **PROGRESS_UPDATE_STYLE)
    message.add(check_heading)

    width = buffered_geo_extent[2] - buffered_geo_extent[0]
    height = buffered_geo_extent[3] - buffered_geo_extent[1]
    try:
        # noinspection PyAugmentAssignment
        width = width / cell_size
        # noinspection PyAugmentAssignment
        height = height / cell_size
    except TypeError:
        # Could have been a vector layer for example
        reason = tr(
            'Computed cellsize was None. Memory check currently only works '
            'for raster input layers.')
        message.add(reason)
        send_message(message)
        return True  # assume enough mem since we have no vector check logic

    bullet_list = m.BulletedList()
    bullet = m.Paragraph(m.ImportantText(tr('Width: ')), str(width))
    bullet_list.add(bullet)
    bullet = m.Paragraph(m.ImportantText(tr('Height: ')), str(height))
    bullet_list.add(bullet)
    bullet = m.Paragraph(m.ImportantText(tr('Cell Size: ')), str(cell_size))
    bullet_list.add(bullet)
    message.add(bullet_list)

    # Compute mem requirement in MB (assuming numpy uses 8 bytes by per
    # cell) see this link:
    # http://stackoverflow.com/questions/11784329/
    #      python-memory-usage-of-numpy-arrays
    # Also note that the on-disk requirement of the clipped tifs is about
    # half this since the tifs as in single precision,
    # whereas numpy arrays are in double precision.
    requirement = ((width * height * 8) / 1024 / 1024)
    try:
        free_memory = get_free_memory()
    except ValueError:
        error_heading = m.Heading(tr('Memory check error'), **WARNING_STYLE)
        error_message = tr('Could not determine free memory')
        message.add(error_heading)
        message.add(error_message)
        send_message(message)
        LOGGER.exception(message)
        return True  # still let the user try to run their analysis

    # We work on the assumption that if more than 10% of the available
    # memory is occupied by a single layer we could run out of memory
    # (depending on the impact function). This is because multiple
    # in memory copies of the layer are often made during processing.
    warning_limit = 10
    usage_indicator = (float(requirement) / float(free_memory)) * 100
    counts_message = tr('Memory requirement: about %d mb per raster layer ('
                        '%d mb available)') % (requirement, free_memory)
    usage_message = tr('Memory used / available: %d/%d') % (usage_indicator,
                                                            warning_limit)
    message.add(counts_message)
    message.add(usage_message)

    if warning_limit <= usage_indicator:
        warning_heading = m.Heading(tr('Potential memory issue'),
                                    **WARNING_STYLE)
        warning_message = tr(
            'There may not be enough free memory to run this analysis. You can'
            ' attempt to run the analysis anyway, but note that your computer '
            'may become unresponsive during execution, and / or the analysis '
            'may fail due to insufficient memory. Proceed at your own risk.')
        suggestion_heading = m.Heading(tr('Suggestion'), **INFO_STYLE)
        suggestion = tr(
            'Try zooming in to a smaller area or using a raster layer with a '
            'coarser resolution to speed up execution and reduce memory '
            'requirements. You could also try adding more RAM to your '
            'computer.')

        message.add(warning_heading)
        message.add(warning_message)
        message.add(suggestion_heading)
        message.add(suggestion)
        send_message(message)
        LOGGER.info(message.to_text())
        return False

    send_message(message)
    LOGGER.info(message.to_text())
    return True
예제 #13
0
 def test_static_message(self):
     """Test we can send static messages to the message viewer."""
     self.message_viewer.static_message_event(None, m.Message('Hi'))
     text = self.message_viewer.page_to_text()
     self.assertEqual(text, 'Hi\n')
예제 #14
0
def impact_attribution(keywords, inasafe_flag=False):
    """Make a little table for attribution of data sources used in impact.

    :param keywords: A keywords dict for an impact layer.
    :type keywords: dict

    :param inasafe_flag: bool - whether to show a little InaSAFE promotional
        text in the attribution output. Defaults to False.

    :returns: An html snippet containing attribution information for the impact
        layer. If no keywords are present or no appropriate keywords are
        present, None is returned.
    :rtype: str
    """
    if keywords is None:
        return None

    myJoinWords = ' - %s ' % tr('sourced from')
    myHazardDetails = tr('Hazard details')
    myHazardTitleKeyword = 'hazard_title'
    myHazardSourceKeyword = 'hazard_source'
    myExposureDetails = tr('Exposure details')
    myExposureTitleKeyword = 'exposure_title'
    myExposureSourceKeyword = 'exposure_source'

    if myHazardTitleKeyword in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        myHazardTitle = safeTr(keywords[myHazardTitleKeyword])
    else:
        myHazardTitle = tr('Hazard layer')

    if myHazardSourceKeyword in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        myHazardSource = safeTr(keywords[myHazardSourceKeyword])
    else:
        myHazardSource = tr('an unknown source')

    if myExposureTitleKeyword in keywords:
        myExposureTitle = keywords[myExposureTitleKeyword]
    else:
        myExposureTitle = tr('Exposure layer')

    if myExposureSourceKeyword in keywords:
        myExposureSource = keywords[myExposureSourceKeyword]
    else:
        myExposureSource = tr('an unknown source')

    myReport = m.Message()
    myReport.add(m.Heading(myHazardDetails, **INFO_STYLE))
    myReport.add(m.Paragraph(myHazardTitle, myJoinWords, myHazardSource))

    myReport.add(m.Heading(myExposureDetails, **INFO_STYLE))
    myReport.add(m.Paragraph(myExposureTitle, myJoinWords, myExposureSource))

    if inasafe_flag:
        myReport.add(m.Heading(tr('Software notes'), **INFO_STYLE))
        myInaSAFEPhrase = tr(
            'This report was created using InaSAFE version %1. Visit '
            'http://inasafe.org to get your free copy of this software!'
            'InaSAFE has been jointly developed by BNPB, AusAid/AIFDRR & the '
            'World Bank').arg(get_version())

        myReport.add(m.Paragraph(m.Text(myInaSAFEPhrase)))
    return myReport
예제 #15
0
def impact_attribution(keywords, inasafe_flag=False):
    """Make a little table for attribution of data sources used in impact.

    :param keywords: A keywords dict for an impact layer.
    :type keywords: dict

    :param inasafe_flag: bool - whether to show a little InaSAFE promotional
        text in the attribution output. Defaults to False.

    :returns: An html snippet containing attribution information for the impact
        layer. If no keywords are present or no appropriate keywords are
        present, None is returned.
    :rtype: safe.messaging.Message
    """
    if keywords is None:
        return None

    join_words = ' - %s ' % tr('sourced from')
    hazard_details = tr('Hazard details')
    hazard_title_keywords = 'hazard_title'
    hazard_source_keywords = 'hazard_source'
    exposure_details = tr('Exposure details')
    exposure_title_keywords = 'exposure_title'
    exposure_source_keyword = 'exposure_source'

    if hazard_title_keywords in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        hazard_title = safeTr(keywords[hazard_title_keywords])
    else:
        hazard_title = tr('Hazard layer')

    if hazard_source_keywords in keywords:
        # We use safe translation infrastructure for this one (rather than Qt)
        hazard_source = safeTr(keywords[hazard_source_keywords])
    else:
        hazard_source = tr('an unknown source')

    if exposure_title_keywords in keywords:
        exposure_title = keywords[exposure_title_keywords]
    else:
        exposure_title = tr('Exposure layer')

    if exposure_source_keyword in keywords:
        exposure_source = keywords[exposure_source_keyword]
    else:
        exposure_source = tr('an unknown source')

    report = m.Message()
    report.add(m.Heading(hazard_details, **INFO_STYLE))
    report.add(m.Paragraph(hazard_title, join_words, hazard_source))

    report.add(m.Heading(exposure_details, **INFO_STYLE))
    report.add(m.Paragraph(exposure_title, join_words, exposure_source))

    if inasafe_flag:
        report.add(m.Heading(tr('Software notes'), **INFO_STYLE))
        # noinspection PyUnresolvedReferences
        inasafe_phrase = tr(
            'This report was created using InaSAFE version %s. Visit '
            'http://inasafe.org to get your free copy of this software!'
            'InaSAFE has been jointly developed by BNPB, AusAid/AIFDRR & the '
            'World Bank') % (get_version())

        report.add(m.Paragraph(m.Text(inasafe_phrase)))
    return report
예제 #16
0
    def _generateTables(self):
        """Parses the postprocessing output as one table per postprocessor.

        Args:
            None

        Returns:
            str - a string containing the html
        """
        myMessage = m.Message()

        for proc, resList in self.postProcessingOutput.iteritems():
            # resList is for example:
            # [
            #    (PyQt4.QtCore.QString(u'Entire area'), OrderedDict([
            #        (u'Total', {'value': 977536, 'metadata': {}}),
            #        (u'Female population', {'value': 508319, 'metadata': {}}),
            #        (u'Weekly hygiene packs', {'value': 403453, 'metadata': {
            #         'description': 'Females hygiene packs for weekly use'}})
            #    ]))
            #]
            try:
                #sorting using the first indicator of a postprocessor
                sortedResList = sorted(resList,
                                       key=self._sortNoData,
                                       reverse=True)

            except KeyError:
                LOGGER.debug('Skipping sorting as the postprocessor did not '
                             'have a "Total" field')

            #init table
            hasNoDataValues = False
            myTable = m.Table(
                style_class='table table-condensed table-striped')
            myTable.caption = self.tr('Detailed %1 report').arg(
                safeTr(get_postprocessor_human_name(proc)).lower())

            myHeaderRow = m.Row()
            myHeaderRow.add(str(self.attributeTitle).capitalize())
            for calculationName in sortedResList[0][1]:
                myHeaderRow.add(self.tr(calculationName))
            myTable.add(myHeaderRow)

            for zoneName, calc in sortedResList:
                myRow = m.Row(zoneName)

                for _, calculationData in calc.iteritems():
                    myValue = calculationData['value']
                    if myValue == self.aggregator.defaults['NO_DATA']:
                        hasNoDataValues = True
                        myValue += ' *'
                    myRow.add(myValue)
                myTable.add(myRow)

            #add table to message
            myMessage.add(myTable)
            if hasNoDataValues:
                myMessage.add(
                    m.EmphasizedText(
                        self.
                        tr('* "%1" values mean that there where some problems while '
                           'calculating them. This did not affect the other '
                           'values.').arg(
                               self.aggregator.defaults['NO_DATA'])))

        try:
            if (self.keywordIO.read_keywords(self.aggregator.layer,
                                             'HAD_MULTIPART_POLY')):
                myMessage.add(
                    m.EmphasizedText(
                        self.
                        tr('The aggregation layer had multipart polygons, these have '
                           'been exploded and are now marked with a #. This has no '
                           'influence on the calculation, just keep in mind that the '
                           'attributes shown may represent the original multipart '
                           'polygon and not the individual exploded polygon parts.'
                           )))
        except Exception:  # pylint: disable=W0703
            pass

        return myMessage
예제 #17
0
    def run(self):
        """Run any post processors requested by the impact function.
        """
        try:
            requested_postprocessors = self.function_parameters[
                'postprocessors']
            postprocessors = get_postprocessors(requested_postprocessors,
                                                self.aggregator.aoi_mode)
        except (TypeError, KeyError):
            # TypeError is for when function_parameters is none
            # KeyError is for when ['postprocessors'] is unavailable
            postprocessors = {}
        LOGGER.debug('Running this postprocessors: ' + str(postprocessors))

        feature_names_attribute = self.aggregator.attributes[
            self.aggregator.get_default_keyword('AGGR_ATTR_KEY')]
        if feature_names_attribute is None:
            self.attribute_title = self.tr('Aggregation unit')
        else:
            self.attribute_title = feature_names_attribute

        name_filed_index = self.aggregator.layer.fieldNameIndex(
            self.attribute_title)
        sum_field_index = self.aggregator.layer.fieldNameIndex(
            self._sum_field_name())

        user_defined_female_ratio = False
        female_ratio_field_index = None
        female_ratio = None
        user_defined_age_ratios = False
        youth_ratio_field_index = None
        youth_ratio = None
        adult_ratio_field_index = None
        adult_ratio = None
        elderly_ratio_field_index = None
        elderly_ratio = None

        if 'Gender' in postprocessors:
            # look if we need to look for a variable female ratio in a layer
            try:
                female_ratio_field = self.aggregator.attributes[
                    self.aggregator.get_default_keyword(
                        'FEMALE_RATIO_ATTR_KEY')]
                female_ratio_field_index = \
                    self.aggregator.layer.fieldNameIndex(female_ratio_field)

                # something went wrong finding the female ratio field,
                # use defaults from below except block
                if female_ratio_field_index == -1:
                    raise KeyError

                user_defined_female_ratio = True

            except KeyError:
                try:
                    female_ratio = self.keyword_io.read_keywords(
                        self.aggregator.layer,
                        self.aggregator.get_default_keyword(
                            'FEMALE_RATIO_KEY'))
                except KeywordNotFoundError:
                    female_ratio = \
                        self.aggregator.get_default_keyword('FEMALE_RATIO')

        if 'Age' in postprocessors:
            # look if we need to look for a variable age ratio in a layer
            try:
                youth_ratio_field = self.aggregator.attributes[
                    self.aggregator.get_default_keyword(
                        'YOUTH_RATIO_ATTR_KEY')]
                youth_ratio_field_index = \
                    self.aggregator.layer.fieldNameIndex(youth_ratio_field)
                adult_ratio_field = self.aggregator.attributes[
                    self.aggregator.get_default_keyword(
                        'ADULT_RATIO_ATTR_KEY')]
                adult_ratio_field_index = \
                    self.aggregator.layer.fieldNameIndex(adult_ratio_field)
                elderly_ratio_field = self.aggregator.attributes[
                    self.aggregator.get_default_keyword(
                        'ELDERLY_RATIO_ATTR_KEY')]
                elderly_ratio_field_index = \
                    self.aggregator.layer.fieldNameIndex(elderly_ratio_field)
                # something went wrong finding the youth ratio field,
                # use defaults from below except block
                if (youth_ratio_field_index == -1
                        or adult_ratio_field_index == -1
                        or elderly_ratio_field_index == -1):
                    raise KeyError

                user_defined_age_ratios = True

            except KeyError:
                try:
                    youth_ratio = self.keyword_io.read_keywords(
                        self.aggregator.layer,
                        self.aggregator.get_default_keyword('YOUTH_RATIO_KEY'))
                    adult_ratio = self.keyword_io.read_keywords(
                        self.aggregator.layer,
                        self.aggregator.get_default_keyword('ADULT_RATIO_KEY'))
                    elderly_ratio = self.keyword_io.read_keywords(
                        self.aggregator.layer,
                        self.aggregator.get_default_keyword(
                            'ELDERLY_RATIO_KEY'))

                except KeywordNotFoundError:
                    youth_ratio = \
                        self.aggregator.get_default_keyword('YOUTH_RATIO')
                    adult_ratio = \
                        self.aggregator.get_default_keyword('ADULT_RATIO')
                    elderly_ratio = \
                        self.aggregator.get_default_keyword('ELDERLY_RATIO')

        if 'BuildingType' or 'RoadType' in postprocessors:
            try:
                key_attribute = self.keyword_io.read_keywords(
                    self.aggregator.exposure_layer, 'key_attribute')
            except KeywordNotFoundError:
                # use 'type' as default
                key_attribute = 'type'

        # iterate zone features
        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        provider = self.aggregator.layer.dataProvider()
        # start data retrieval: fetch no geometry and all attributes for each
        # feature
        polygon_index = 0
        for feature in provider.getFeatures(request):
            # if a feature has no field called
            if name_filed_index == -1:
                zone_name = str(feature.id())
            else:
                zone_name = feature[name_filed_index]

            # create dictionary of attributes to pass to postprocessor
            general_params = {
                'target_field': self.aggregator.target_field,
                'function_params': self.function_parameters
            }

            if self.aggregator.statistics_type == 'class_count':
                general_params['impact_classes'] = (
                    self.aggregator.statistics_classes)
            elif self.aggregator.statistics_type == 'sum':
                impact_total = feature[sum_field_index]
                general_params['impact_total'] = impact_total

            try:
                general_params['impact_attrs'] = (
                    self.aggregator.impact_layer_attributes[polygon_index])
            except IndexError:
                # rasters and attributeless vectors have no attributes
                general_params['impact_attrs'] = None

            for key, value in postprocessors.iteritems():
                parameters = general_params
                try:
                    # look if params are available for this postprocessor
                    parameters.update(
                        self.function_parameters['postprocessors'][key]
                        ['params'])
                except KeyError:
                    pass

                if key == 'Gender':
                    if user_defined_female_ratio:
                        female_ratio = feature[female_ratio_field_index]
                        if female_ratio is None:
                            female_ratio = self.aggregator.defaults[
                                'FEMALE_RATIO']
                            LOGGER.warning('Data Driven Female ratio '
                                           'incomplete, using defaults for'
                                           ' aggregation unit'
                                           ' %s' % feature.id)

                    parameters['female_ratio'] = female_ratio

                if key == 'Age':
                    if user_defined_age_ratios:
                        youth_ratio = feature[youth_ratio_field_index]
                        adult_ratio = feature[adult_ratio_field_index]
                        elderly_ratio = feature[elderly_ratio_field_index]
                        if (youth_ratio is None or adult_ratio is None
                                or elderly_ratio is None):
                            youth_ratio = self.aggregator.defaults[
                                'YOUTH_RATIO']
                            adult_ratio = self.aggregator.defaults[
                                'ADULT_RATIO']
                            elderly_ratio = self.aggregator.defaults[
                                'ELDERLY_RATIO']
                            LOGGER.warning('Data Driven Age ratios '
                                           'incomplete, using defaults for'
                                           ' aggregation unit'
                                           ' %s' % feature.id)

                    parameters['youth_ratio'] = youth_ratio
                    parameters['adult_ratio'] = adult_ratio
                    parameters['elderly_ratio'] = elderly_ratio

                if key == 'BuildingType' or key == 'RoadType':
                    parameters['key_attribute'] = key_attribute

                try:
                    value.setup(parameters)
                    value.process()
                    results = value.results()
                    value.clear()
                    # LOGGER.debug(results)

                    # this can raise a KeyError
                    self.output[key].append((zone_name, results))

                except PostProcessorError as e:
                    message = m.Message(
                        m.Heading(self.tr('%s postprocessor problem' % key),
                                  **styles.DETAILS_STYLE),
                        m.Paragraph(self.tr(str(e))))
                    self.error_message = message

                except KeyError:
                    self.output[key] = []
                    self.output[key].append((zone_name, results))
            # increment the index
            polygon_index += 1