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)
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)
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)
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)
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)
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)
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
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)
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)
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
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
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')
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
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
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
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