def validate_input(self): """Validate the input before saving a scenario. Those validations are: 1. self.exposure_layer must be not None 2. self.hazard_layer must be not None 3. self.function_id is not an empty string or None """ self.exposure_layer = self.dock.get_exposure_layer() self.hazard_layer = self.dock.get_hazard_layer() self.aggregation_layer = self.dock.get_aggregation_layer() is_valid = True warning_message = None if self.exposure_layer is None: warning_message = tr( 'Exposure layer is not found, can not save scenario. Please ' 'add exposure layer to do so.') is_valid = False if self.hazard_layer is None: warning_message = tr( 'Hazard layer is not found, can not save scenario. Please add ' 'hazard layer to do so.') is_valid = False return is_valid, warning_message
def show_keywords_need_review_message(sender, message=None): """Show a message keywords are not adequate to run an analysis. .. versionadded: 4.0 :param message: Additional message to display. :type message: str .. note:: The print button will be disabled if this method is called. """ LOGGER.debug('Showing incorrect keywords for v4 message') message = generate_input_error_message( tr('Layer Keywords Outdated:'), m.Paragraph( tr( 'Please update the keywords for your layers and then ' 'try to run the analysis again. Use the keyword wizard '), m.Image( 'file:///%s/img/icons/' 'show-keyword-wizard.svg' % resources_path(), **SMALL_ICON_STYLE), tr( ' icon in the toolbar to update your layer\'s keywords.'), message) ) send_static_message(sender, message)
def threshold(): """Generator for the default threshold parameter. :return: List of InputListParameter :rtype: list[InputListParameter] """ field = InputListParameter() field.name = 'Thresholds [m]' field.is_required = True field.element_type = float field.expected_type = list field.ordering = InputListParameter.AscendingOrder field.minimum_item_count = 1 # Rizky: no reason for the number below. It can be any values to describe # maximum item count. Feel free to change it when necessary. # PS: it was my birthdate field.maximum_item_count = 19 field.value = [1.0] # default value field.help_text = tr( 'Thresholds value to categorize inundated area.') field.description = tr( 'Up to three thresholds (in meters) can be set in an increasing ' 'order. The impact function will report the number of people per ' 'threshold you define here. Specify the upper bound for each ' 'threshold. The lower bound of the first threshold shall be zero. ' 'People in water depths above the maximum threshold will be ' 'classified as needing evacuation.') return field
def get_metadata(): """Return metadata as a dictionary. This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'ContinuousHazardPopulationImpactFunction', 'name': tr('Continuous Hazard Population Impact Function'), 'impact': tr('Be impacted'), 'author': 'AIFDR', 'date_implemented': 'N/A', 'overview': tr( 'To assess the impacts of continuous hazards in raster ' 'format on population raster layer.'), 'categories': { 'hazard': { 'definition': hazard_definition, 'subcategories': hazard_all, # already a list 'units': [], 'layer_constraints': [layer_raster_continuous] }, 'exposure': { 'definition': exposure_definition, 'subcategories': [exposure_population], 'units': [unit_people_per_pixel], 'layer_constraints': [layer_raster_continuous] } } } return dict_meta
def show_keyword_version_message(sender, keyword_version, inasafe_version): """Show a message indicating that the keywords version is mismatch .. versionadded: 3.2 :param keyword_version: The version of the layer's keywords :type keyword_version: str :param inasafe_version: The version of the InaSAFE :type inasafe_version: str .. note:: The print button will be disabled if this method is called. """ LOGGER.debug('Showing Mismatch Version Message') message = generate_input_error_message( tr('Layer Keyword\'s Version Mismatch:'), m.Paragraph( tr( 'Your layer\'s keyword\'s version ({layer_version}) does not ' 'match with your InaSAFE version ({inasafe_version}). If you ' 'wish to use it as an exposure, hazard, or aggregation layer ' 'in an analysis, please use the keyword wizard to update the ' 'keywords. You can open the wizard by clicking on ' 'the ').format( layer_version=keyword_version, inasafe_version=inasafe_version), m.Image( 'file:///%s/img/icons/' 'show-keyword-wizard.svg' % resources_path(), **SMALL_ICON_STYLE), tr( ' icon in the toolbar.')) ) send_static_message(sender, message)
def _affected_categories(self): """Overwriting the affected categories, since 'unaffected' are counted. :returns: The categories that equal affected. :rtype: list """ return [tr('Flooded'), tr('Wet')]
def notes(self): """Return the notes section of the report. :return: The notes that should be attached to this impact report. :rtype: list """ volcano_names = self.volcano_names return [ { 'content': tr('Notes'), 'header': True }, { 'content': tr( 'Map shows buildings affected in each of the ' 'volcano buffered zones.') }, { 'content': tr( 'Only buildings available in OpenStreetMap ' 'are considered.') }, { 'content': tr('Volcanoes considered: %s.') % volcano_names, 'header': True } ]
def as_dict(): """Return metadata as a dictionary. This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'FloodRasterRoadsFunction', 'name': tr('Raster flood on roads'), 'impact': tr('Be flooded in given thresholds'), 'title': tr('Be flooded in given thresholds'), 'function_type': 'qgis2.0', 'author': 'Dmitry Kolesov', 'date_implemented': 'N/A', 'overview': tr('N/A'), 'detailed_description': '', 'hazard_input': '', 'exposure_input': '', 'output': '', 'actions': '', 'limitations': [], 'citations': [], 'layer_requirements': { 'hazard': { 'layer_mode': layer_mode_continuous, 'layer_geometries': [layer_geometry_raster], 'hazard_categories': [ hazard_category_single_event, hazard_category_multiple_event ], 'hazard_types': [hazard_flood], 'continuous_hazard_units': [unit_feet, unit_metres], 'vector_hazard_classifications': [], 'raster_hazard_classifications': [], 'additional_keywords': [] }, 'exposure': { 'layer_mode': layer_mode_classified, 'layer_geometries': [layer_geometry_line], 'exposure_types': [exposure_road], 'exposure_units': [], 'exposure_class_fields': [road_class_field], 'additional_keywords': [] } }, 'parameters': OrderedDict([ ('min threshold', parameter_definitions.min_threshold()), ('max threshold', parameter_definitions.max_threshold()), ('postprocessors', OrderedDict([ ('RoadType', road_type_postprocessor()) ])) ]) } return dict_meta
def notes(self): """Return the notes section of the report. :return: The notes that should be attached to this impact report. :rtype: list """ if get_needs_provenance_value(self.parameters) is None: needs_provenance = '' else: needs_provenance = tr(get_needs_provenance_value(self.parameters)) fields = [ tr('Map shows buildings affected in each of the volcano buffered ' 'zones.'), tr('Total population in the analysis area: %s') % population_rounding(self.total_population), tr('<sup>1</sup>People need evacuation if they are within the ' 'volcanic hazard zones.'), tr('Volcanoes considered: %s.') % self.volcano_names, needs_provenance ] if self.no_data_warning: fields = fields + no_data_warning # include any generic exposure specific notes from definitions.py fields = fields + self.exposure_notes() # include any generic hazard specific notes from definitions.py fields = fields + self.hazard_notes() return fields
def notes(self): """Return the notes section of the report. :return: The notes that should be attached to this impact report. :rtype: list """ if get_needs_provenance_value(self.parameters) is None: needs_provenance = '' else: needs_provenance = tr(get_needs_provenance_value(self.parameters)) if self.volcano_names: sorted_volcano_names = ', '.join(sorted(self.volcano_names)) else: sorted_volcano_names = tr('Not specified in data') fields = [ tr('Total population in the analysis area: %s') % format_int(population_rounding(self.total_population)), tr('<sup>1</sup>People need evacuation if they are within the ' 'volcanic hazard zones.'), tr('Volcanoes considered: %s.') % sorted_volcano_names ] if needs_provenance: fields.append(needs_provenance) if self.no_data_warning: fields = fields + no_data_warning # include any generic exposure specific notes from definitions.py fields = fields + self.exposure_notes() # include any generic hazard specific notes from definitions.py fields = fields + self.hazard_notes() return fields
def notes(self): """Return the notes section of the report. :return: The notes that should be attached to this impact report. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') message.add( m.Heading(tr('Notes and assumptions'), **styles.INFO_STYLE)) checklist = m.BulletedList() # Thresholds for mmi breakdown. t0 = self.parameters['low_threshold'].value t1 = self.parameters['medium_threshold'].value t2 = self.parameters['high_threshold'].value is_nexis = self.is_nexis checklist.add(tr( 'High hazard is defined as shake levels greater ' 'than %i on the MMI scale.') % t2) checklist.add(tr( 'Medium hazard is defined as shake levels ' 'between %i and %i on the MMI scale.') % (t1, t2)) checklist.add(tr( 'Low hazard is defined as shake levels ' 'between %i and %i on the MMI scale.') % (t0, t1)) if is_nexis: checklist.add(tr( 'Values are in units of 1 million Australian Dollars')) message.add(checklist) return message
def lookup_category(self, category): """Lookup a category by its name. :param category: The category to be looked up. :type category: basestring :returns: The category's count. :rtype: int .. note:: The category may be any valid category, but it also includes 'Population Not Affected', 'Unaffected Population' for unaffected as well as 'Total Impacted', 'People impacted', 'Total Population Affected' for total affected population. This diversity is to accodate existing usages, which have evolved separately. We may want to update these when we have decided on a single convention. """ if category in self.affected_population.keys(): return self.affected_population[category] if category in self.other_population_counts.keys(): return self.other_population_counts[category] if category in [ tr('Population Not Affected'), tr('Unaffected Population')]: return self.unaffected_population if category in [ tr('Total Impacted'), tr('People impacted'), tr('Total Population Affected')]: return self.total_affected_population
def add_new_resource(self): """Handle add new resource requests. """ parameters_widget = [ self.parameters_scrollarea.layout().itemAt(i) for i in range(self.parameters_scrollarea.layout().count())][0].widget() parameter_widgets = [ parameters_widget.vertical_layout.itemAt(i).widget() for i in range(parameters_widget.vertical_layout.count())] parameter_widgets[0].set_text('') parameter_widgets[1].set_text('') parameter_widgets[2].set_text('') parameter_widgets[3].set_text('') parameter_widgets[4].set_text('') parameter_widgets[5].set_value(10) parameter_widgets[6].set_value(0) parameter_widgets[7].set_value(100) parameter_widgets[8].set_text(tr('weekly')) parameter_widgets[9].set_text(tr( "A displaced person should be provided with " "{{ Default }} {{ Unit }}/{{ Units }}/{{ Unit abbreviation }} of " "{{ Resource name }}. Though no less than {{ Minimum allowed }} " "and no more than {{ Maximum allowed }}. This should be provided " "{{ Frequency }}.")) self.stacked_widget.setCurrentWidget(self.resource_edit_page) # hide the close button self.button_box.button(QDialogButtonBox.Close).setHidden(True)
def metadata_converter_help_content(): """Helper method that returns just the content in extent mode. This method was added so that the text could be reused in the wizard. :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() paragraph = m.Paragraph(tr( 'This tool will convert InaSAFE 4.x keyword metadata into the ' 'metadata format used by InaSAFE 3.5. The primary reason for doing ' 'this is to prepare data for use in GeoSAFE - the online version of ' 'InaSAFE.' )) message.add(paragraph) paragraph = m.Paragraph(tr( 'You should note that this tool will not touch the original data or ' 'metadata associated with a layer. Instead it will make a copy of the ' 'original layer to the place that you nominate, and create a new ' 'keywords XML file to accompany that data. This new keywords file ' 'will contain InaSAFE keywords in the 3.5 format.' )) message.add(paragraph) return message
def minimum_needs_breakdown(self): """Breakdown by population. :returns: The population breakdown report. :rtype: list """ message = m.Message(style_class='container') message.add(m.Heading( tr('Evacuated population minimum needs'), **styles.INFO_STYLE)) table = m.Table( style_class='table table-condensed table-striped') table.caption = None total_needs = self.total_needs for frequency, needs in total_needs.items(): row = m.Row() row.add(m.Cell( tr('Relief items to be provided %s' % frequency), header=True )) row.add(m.Cell(tr('Total'), header=True, align='right')) table.add(row) for resource in needs: row = m.Row() row.add(m.Cell(tr(resource['table name']))) row.add(m.Cell( tr(format_int(resource['amount'])), align='right' )) table.add(row) message.add(table) return message
def format_impact_summary(self): """The impact summary as per category :returns: The impact summary. :rtype: safe.message.Message """ attributes = self.impact_summary['attributes'] fields = self.impact_summary['fields'] message = m.Message(style_class='container') table = m.Table(style_class='table table-condensed table-striped') table.caption = None row = m.Row() row.add(m.Cell(tr('Summary by road type'), header=True)) for _ in attributes: row.add(m.Cell('', header=True)) row = m.Row() row.add(m.Cell(tr('Road Type'), header=True)) for affected_category in attributes: row.add(m.Cell(tr(affected_category), header=True, align='right')) table.add(row) row = m.Row() row.add(m.Cell(tr('All (m)'))) for total_affected_value in fields[0]: row.add(m.Cell( format_int(int(total_affected_value)), align='right')) table.add(row) message.add(table) return message
def action_checklist(self): """Action checklist for the itb earthquake fatality report. :returns: The action checklist :rtype: list """ total_fatalities = self.total_fatalities total_displaced = self.total_evacuated rounded_displaced = format_int(population_rounding(total_displaced)) fields = super(ITBFatalityFunction, self).action_checklist() if total_fatalities: fields.append(tr( 'Are there enough victim identification units available ' 'for %s people?') % ( format_int(population_rounding(total_fatalities)))) if total_displaced: fields.append(tr( 'Are there enough shelters and relief items available for ' '%s people?') % rounded_displaced) if rounded_displaced: fields.append(tr( 'If yes, where are they located and how will we ' 'distribute them?')) if total_displaced: fields.append(tr( 'If no, where can we obtain additional relief items ' 'from and how will we transport them?')) return fields
def get_map_title(hazard, exposure, hazard_category): """Helper to get map title. :param hazard: A hazard definition. :type hazard: dict :param exposure: An exposure definition. :type exposure: dict :param hazard_category: A hazard category definition. :type hazard_category: dict :returns: Map title based on the input. :rtype: str """ if hazard == hazard_generic: map_title = tr('{exposure_name} affected').format( exposure_name=exposure['name']) else: if hazard_category == hazard_category_single_event: map_title = tr( '{exposure_name} affected by {hazard_name} event').format( exposure_name=exposure['name'], hazard_name=hazard['name']) else: map_title = tr( '{exposure_name} affected by {hazard_name} hazard').format( exposure_name=exposure['name'], hazard_name=hazard['name']) return map_title
def get_analysis_question(hazard, exposure): """Construct analysis question based on hazard and exposure. :param hazard: A hazard definition. :type hazard: dict :param exposure: An exposure definition. :type exposure: dict :returns: Analysis question based on reporting standards. :rtype: str """ # First we look for a translated hardcoded question. question = specific_analysis_question(hazard, exposure) if question: return question if hazard == hazard_generic: # Secondly, if the hazard is generic, we don't need the hazard. question = tr( 'In each of the hazard zones {exposure_measure} {exposure_name} ' 'might be affected?').format( exposure_measure=exposure['measure_question'], exposure_name=exposure['name']) return question # Then, we fallback on a generated string on the fly. question = tr( 'In the event of a {hazard_name}, {exposure_measure} {exposure_name} ' 'might be affected?').format( hazard_name=hazard['name'], exposure_measure=exposure['measure_question'], exposure_name=exposure['name']) return question
def impact_summary_headings(self): """Headings for the impact summary. :return: Headings :rtype: list """ return [tr('Buildings'), tr('Count')]
def impact_summary(self): """Create impact summary as data. :returns: Impact Summary in dictionary format. :rtype: dict """ affect_types = self._impact_breakdown attributes = ['category', 'value'] fields = [] for (category, building_breakdown) in self.affected_buildings.items(): total_affected = [0] * len(affect_types) for affected_breakdown in building_breakdown.values(): for affect_type, number_affected in affected_breakdown.items(): count = affect_types.index(affect_type) total_affected[count] += number_affected field = [tr(category)] for affected in total_affected: field.append(affected) fields.append(field) if len(self._affected_categories) > 1: fields.append( [tr('Affected buildings'), self.total_affected_buildings]) if self._affected_categories == self.affected_buildings.keys(): fields.append([ tr('Not affected buildings'), self.total_unaffected_buildings] ) fields.append([tr('Total'), self.total_buildings]) return { 'attributes': attributes, 'fields': fields }
def get_metadata(): """Return metadata as a dictionary. This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'FloodNativePolygonExperimentalFunction', 'name': tr('Flood Native Polygon Experimental Function'), 'impact': tr('Be-flooded'), 'author': 'Dmitry Kolesov', 'date_implemented': 'N/A', 'overview': tr('N/A'), 'categories': { 'hazard': { 'definition': hazard_definition, 'subcategories': [hazard_flood], 'units': [unit_wetdry], 'layer_constraints': [layer_vector_polygon] }, 'exposure': { 'definition': exposure_definition, 'subcategories': [exposure_structure], 'units': [unit_building_type_type], 'layer_constraints': [layer_vector_polygon] } } } return dict_meta
def get_metadata(): """Return metadata as a dictionary This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'ITBFatalityFunction', 'name': tr('ITB Fatality Function'), 'impact': tr('Die or be displaced'), 'author': 'Hadi Ghasemi', 'date_implemented': 'N/A', 'overview': tr( 'To assess the impact of earthquake on population based ' 'on earthquake model developed by ITB'), 'categories': { 'hazard': { 'definition': hazard_definition, 'subcategories': [hazard_earthquake], 'units': [unit_mmi], 'layer_constraints': [layer_raster_continuous] }, 'exposure': { 'definition': exposure_definition, 'subcategories': [exposure_population], 'units': [unit_people_per_pixel], 'layer_constraints': [layer_raster_continuous] } } } return dict_meta
def minimum_needs_breakdown(self): """Breakdown by building type. :returns: The buildings breakdown report. :rtype: list """ minimum_needs_breakdown_report = [{ 'content': tr('Evacuated population minimum needs'), 'header': True }] total_needs = self.total_needs for frequency, needs in total_needs.items(): minimum_needs_breakdown_report.append( { 'content': [ tr('Needs that should be provided %s' % frequency), tr('Total')], 'header': True }) for resource in needs: minimum_needs_breakdown_report.append( { 'content': [ tr(resource['table name']), tr(format_int(resource['amount']))] }) return minimum_needs_breakdown_report
def set_widgets(self): """Set widgets on the Aggregation Layer Origin Type tab""" # First, list available layers in order to check if there are # any available layers. Note This will be repeated in # set_widgets_step_fc_agglayer_from_canvas because we need # to list them again after coming back from the Keyword Wizard. self.parent.step_fc_agglayer_from_canvas.\ list_compatible_canvas_layers() lst_wdg = self.parent.step_fc_agglayer_from_canvas.lstCanvasAggLayers if lst_wdg.count(): self.rbAggLayerFromCanvas.setText(tr( 'I would like to use an aggregation layer already loaded in ' 'QGIS\n' '(launches the %s for aggregation if needed)' ) % self.parent.keyword_creation_wizard_name) self.rbAggLayerFromCanvas.setEnabled(True) self.rbAggLayerFromCanvas.click() else: self.rbAggLayerFromCanvas.setText(tr( 'I would like to use an aggregation layer already loaded in ' 'QGIS\n' '(no suitable layers found)')) self.rbAggLayerFromCanvas.setEnabled(False) self.rbAggLayerFromBrowser.click() # Set icon self.lblIconIFCWAggregationOrigin.setPixmap(QPixmap(None))
def content(): """Helper method that returns just the content. This method was added so that the text could be reused in the dock_help module. .. versionadded:: 3.2.2 :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() message.add(m.Paragraph(tr( 'This tool will calculated minimum needs for evacuated people. To ' 'use this tool effectively:' ))) tips = m.BulletedList() tips.add(tr( 'Load a point or polygon layer in QGIS. Typically the layer will ' 'represent administrative districts where people have gone to an ' 'evacuation center.')) tips.add(tr( 'Ensure that the layer has an INTEGER attribute for the number of ' 'displaced people associated with each feature.' )) tips.add(tr( 'Use the pick lists below to select the layer and the population ' 'field and then press \'OK\'.' )) tips.add(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.add(tips) return message
def get_metadata(): """Return metadata as a dictionary. This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'FloodEvacuationFunctionVectorHazard', 'name': tr('Flood Evacuation Function Vector Hazard'), 'impact': tr('Need evacuation'), 'author': 'AIFDR', 'date_implemented': 'N/A', 'overview': tr( 'To assess the impacts of flood inundation ' 'in vector format on population.'), 'categories': { 'hazard': { 'definition': hazard_definition, 'subcategories': [hazard_flood], 'units': [unit_wetdry], 'layer_constraints': [layer_vector_polygon] }, 'exposure': { 'definition': exposure_definition, 'subcategories': [exposure_population], 'units': [unit_people_per_pixel], 'layer_constraints': [layer_raster_continuous] } } } return dict_meta
def _affected_categories(self): """Overwriting the affected categories, since 'unaffected' are counted. :returns: The categories that equal effected. :rtype: list """ return [tr('Number Inundated'), tr('Number of Wet Buildings')]
def save_current_keywords(self): """Save keywords to the layer. It will write out the keywords for the current layer. This method is based on the KeywordsDialog class. """ current_keywords = self.get_keywords() try: self.keyword_io.write_keywords( layer=self.layer, keywords=current_keywords) except InaSAFEError as e: error_message = get_error_message(e) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QMessageBox.warning( self, tr('InaSAFE'), tr('An error was encountered when saving the following ' 'keywords:\n {error_message}').format( error_message=error_message.to_html())) if self.dock is not None: # noinspection PyUnresolvedReferences self.dock.get_layers() # Save default value to QSetting if current_keywords.get('inasafe_default_values'): for key, value in ( list(current_keywords['inasafe_default_values'].items())): set_inasafe_default_value_qsetting( self.setting, RECENT, key, value)
def notes(self): """Return the notes section of the report. .. versionadded: 3.2.1 :return: The notes that should be attached to this impact report. :rtype: list """ threshold = self.parameters['min threshold'].value hazard = self.hazard.keyword('hazard') hazard_terminology = tr('flooded') hazard_object = tr('flood') if hazard == 'flood': # Use flooded pass elif hazard == 'tsunami': hazard_terminology = tr('inundated') hazard_object = tr('water') fields = [ tr('Roads are %s when %s levels exceed %.2f m.') % (hazard_terminology, hazard_object, threshold), ] # include any generic exposure specific notes from definitions.py fields = fields + self.exposure_notes() # include any generic hazard specific notes from definitions.py fields = fields + self.hazard_notes() return fields
def read_from_file(self, filename): """Read and unpack vector data. It is assumed that the file contains only one layer with the pertinent features. Further it is assumed for the moment that all geometries are points. * A feature is a geometry and a set of attributes. * A geometry refers to location and can be point, line, polygon or combinations thereof. * The attributes or obtained through GetField() The full OGR architecture is documented at * http://www.gdal.org/ogr/ogr_arch.html * http://www.gdal.org/ogr/ogr_apitut.html Examples are at * danieljlewis.org/files/2010/09/basicpythonmap.pdf * http://invisibleroads.com/tutorials/gdal-shapefile-points-save.html * http://www.packtpub.com/article/geospatial-data-python-geometry Limitation of the Shapefile are documented in http://resources.esri.com/help/9.3/ArcGISDesktop/com/Gp_ToolRef/ geoprocessing_tool_reference/ geoprocessing_considerations_for_shapefile_output.htm :param filename: a fully qualified location to the file :type filename: str :raises: ReadLayerError """ base_name = os.path.splitext(filename)[0] # Look for any keywords self.keywords = read_keywords(base_name + '.keywords') # FIXME (Ole): Should also look for style file to populate style_info # Determine name if 'title' in self.keywords: title = self.keywords['title'] # Lookup internationalised title if available title = tr(title) vector_name = title else: # Use base_name without leading directories as name vector_name = os.path.split(base_name)[-1] if self.name is None: self.name = vector_name self.filename = filename self.geometry_type = None # In case there are no features fid = ogr.Open(filename) if fid is None: msg = 'Could not open %s' % filename raise ReadLayerError(msg) # Assume that file contains all data in one layer msg = 'Only one vector layer currently allowed' if fid.GetLayerCount() > 1 and self.sublayer is None: msg = ('WARNING: Number of layers in %s are %i. ' 'Only the first layer will currently be ' 'used. Specify sublayer when creating ' 'the Vector if you wish to use a different layer.' % (filename, fid.GetLayerCount())) LOGGER.warn(msg) # Why do we raise an exception if it is only a warning? TS raise ReadLayerError(msg) if self.sublayer is not None: layer = fid.GetLayerByName(self.sublayer) else: layer = fid.GetLayerByIndex(0) # Get spatial extent self.extent = layer.GetExtent() # Get projection p = layer.GetSpatialRef() self.projection = Projection(p) layer.ResetReading() # Extract coordinates and attributes for all features geometry = [] data = [] # Use feature iterator for feature in layer: # Record coordinates ordered as Longitude, Latitude G = feature.GetGeometryRef() if G is None: msg = ('Geometry was None in filename %s ' % filename) raise ReadLayerError(msg) else: self.geometry_type = G.GetGeometryType() if self.is_point_data: geometry.append((G.GetX(), G.GetY())) elif self.is_line_data: ring = get_ring_data(G) geometry.append(ring) elif self.is_polygon_data: polygon = get_polygon_data(G) geometry.append(polygon) elif self.is_multi_polygon_data: try: G = ogr.ForceToPolygon(G) except: msg = ('Got geometry type Multipolygon (%s) for ' 'filename %s and could not convert it to ' 'singlepart. However, you can use QGIS ' 'functionality to convert multipart vector ' 'data to singlepart (Vector -> Geometry Tools ' '-> Multipart to Singleparts and use the ' 'resulting dataset.' % (ogr.wkbMultiPolygon, filename)) raise ReadLayerError(msg) else: # Read polygon data as single part self.geometry_type = ogr.wkbPolygon polygon = get_polygon_data(G) geometry.append(polygon) else: msg = ('Only point, line and polygon geometries are ' 'supported. ' 'Geometry type in filename %s ' 'was %s.' % (filename, self.geometry_type)) raise ReadLayerError(msg) # Record attributes by name number_of_fields = feature.GetFieldCount() fields = {} for j in range(number_of_fields): name = feature.GetFieldDefnRef(j).GetName() # FIXME (Ole): Ascertain the type of each field? # We need to cast each appropriately? # This is issue #66 # (https://github.com/AIFDR/riab/issues/66) # feature_type = feature.GetFieldDefnRef(j).GetType() fields[name] = feature.GetField(j) # We do this because there is NaN problem on windows # NaN value must be converted to _pseudo_in to solve the # problem. But, when InaSAFE read the file, it'll be # converted back to NaN value, so that NaN in InaSAFE is a # numpy.nan # please check https://github.com/AIFDR/inasafe/issues/269 # for more information if fields[name] == _pseudo_inf: fields[name] = float('nan') # print 'Field', name, feature_type, j, fields[name] data.append(fields) # Store geometry coordinates as a compact numeric array self.geometry = geometry self.data = data
def save_scenario(self, scenario_file_path=None): """Save current scenario to a text file. You can use the saved scenario with the batch runner. :param scenario_file_path: A path to the scenario file. :type scenario_file_path: str """ # Validate Input warning_title = tr('InaSAFE Save Scenario Warning') is_valid, warning_message = self.validate_input() if not is_valid: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QMessageBox.warning(self, warning_title, warning_message) return # Make extent to look like: # 109.829170982, -8.13333290561, 111.005344795, -7.49226294379 # Added in 2.2 to support user defined analysis extents if self.dock.extent.user_extent is not None \ and self.dock.extent.crs is not None: # In V4.0, user_extent is QgsGeometry. user_extent = self.dock.extent.user_extent.boundingBox() extent = extent_to_array(user_extent, self.dock.extent.crs) else: extent = viewport_geo_array(self.iface.mapCanvas()) extent_string = ', '.join(('%f' % x) for x in extent) exposure_path = self.exposure_layer.source() hazard_path = self.hazard_layer.source() title = self.keyword_io.read_keywords(self.hazard_layer, 'title') title = tr(title) default_filename = title.replace(' ', '_').replace('(', '').replace(')', '') # Popup a dialog to request the filename if scenario_file_path = None dialog_title = tr('Save Scenario') if scenario_file_path is None: # noinspection PyCallByClass,PyTypeChecker scenario_file_path, __ = QFileDialog.getSaveFileName( self, dialog_title, os.path.join(self.output_directory, default_filename + '.txt'), "Text files (*.txt)") if scenario_file_path is None or scenario_file_path == '': return self.output_directory = os.path.dirname(scenario_file_path) # Write to file parser = ConfigParser() parser.add_section(title) # Relative path is not recognized by the batch runner, so we use # absolute path. parser.set(title, 'exposure', exposure_path) parser.set(title, 'hazard', hazard_path) parser.set(title, 'extent', extent_string) if self.dock.extent.crs is None: parser.set(title, 'extent_crs', 'EPSG:4326') else: parser.set(title, 'extent_crs', self.dock.extent.crs.authid()) if self.aggregation_layer is not None: aggregation_path = self.aggregation_layer.source() relative_aggregation_path = self.relative_path( scenario_file_path, aggregation_path) parser.set(title, 'aggregation', relative_aggregation_path) # noinspection PyBroadException try: of = open(scenario_file_path, 'a') parser.write(of) of.close() except Exception as e: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList QMessageBox.warning( self, 'InaSAFE', tr('Failed to save scenario to {path}, exception ' '{exception}').format(path=scenario_file_path, exception=str(e))) finally: of.close() # Save State self.save_state()
# coding=utf-8 """Definitions relating to API that used in Peta Bencana downloader.""" from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'bd00bfeac510722b427544b186bfa10861749e51' development_api = { 'key': 'development_api', 'name': tr('Development API'), 'url': 'https://data-dev.petabencana.id/floods' '?city={city_code}&geoformat=geojson&format=json&minimum_state=1', 'help_url': 'https://docs.petabencana.id/', 'available_data': [{ 'code': 'jbd', 'name': 'Jabodetabek' }, { 'code': 'bdg', 'name': 'Bandung' }, { 'code': 'sby', 'name': 'Surabaya' }] }
def _value_maps_row(value_maps_keyword): """Helper to make a message row from a value maps. Expected keywords: 'value_maps': { 'structure': { 'ina_structure_flood_hazard_classification': { 'classes': { 'low': [1, 2, 3], 'medium': [4], 'high': [5, 6] }, 'active': True }, 'ina_structure_flood_hazard_4_class_classification': { 'classes': { 'low': [1], 'medium': [2, 3, 4], 'high': [5, 6, 7], 'very_high': [8] }, 'active': False } }, 'population': { 'ina_population_flood_hazard_classification': { 'classes': { 'low': [1], 'medium': [2, 3], 'high': [4, 5, 6] }, 'active': False }, 'ina_population_flood_hazard_4_class_classification': { 'classes': { 'low': [1, 2], 'medium': [3, 4], 'high': [4, 5, 6], 'very_high': [6, 7, 8] }, 'active': True } }, } :param value_maps_keyword: Value of the keyword to be rendered. This must be a string representation of a dict, or a dict. :type value_maps_keyword: basestring, dict :returns: A table to be added into a cell in the keywords table. :rtype: safe.messaging.items.table """ if isinstance(value_maps_keyword, basestring): value_maps_keyword = literal_eval(value_maps_keyword) table = m.Table(style_class='table table-condensed table-striped') i = 0 for exposure_key, classifications in value_maps_keyword.items(): i += 1 exposure = definition(exposure_key) exposure_row = m.Row() exposure_row.add(m.Cell(m.ImportantText(tr('Exposure')))) exposure_row.add(m.Cell(exposure['name'])) table.add(exposure_row) classification_row = m.Row() classification_row.add(m.Cell(m.ImportantText(tr( 'Classification')))) active_classification = None for classification, value in classifications.items(): if value.get('active'): active_classification = definition(classification) if active_classification.get('name'): classification_row.add( m.Cell(active_classification['name'])) break if not active_classification: classification_row.add(m.Cell(tr('No classifications set.'))) continue table.add(classification_row) header = m.Row() header.add(m.Cell(tr('Class name'))) header.add(m.Cell(tr('Values'))) table.add(header) classes = active_classification.get('classes') # Sort by value, put the lowest first classes = sorted(classes, key=lambda k: k['value']) for the_class in classes: value_map = classifications[active_classification['key']][ 'classes'].get(the_class['key'], []) row = m.Row() row.add(m.Cell(the_class['name'])) row.add(m.Cell(', '.join([str(v) for v in value_map]))) table.add(row) if i < len(value_maps_keyword): # Empty row empty_row = m.Row() empty_row.add(m.Cell('')) empty_row.add(m.Cell('')) table.add(empty_row) return table
def _threshold_to_row(thresholds_keyword): """Helper to make a message row from a threshold We are expecting something like this: { 'thresholds': { 'structure': { 'ina_structure_flood_hazard_classification': { 'classes': { 'low': [1, 2], 'medium': [3, 4], 'high': [5, 6] }, 'active': True }, 'ina_structure_flood_hazard_4_class_classification': { 'classes': { 'low': [1, 2], 'medium': [3, 4], 'high': [5, 6], 'very_high': [7, 8] }, 'active': False } }, 'population': { 'ina_population_flood_hazard_classification': { 'classes': { 'low': [1, 2.5], 'medium': [2.5, 4.5], 'high': [4.5, 6] }, 'active': False }, 'ina_population_flood_hazard_4_class_classification': { 'classes': { 'low': [1, 2.5], 'medium': [2.5, 4], 'high': [4, 6], 'very_high': [6, 8] }, 'active': True } }, }, Each value is a list with exactly two element [a, b], where a <= b. :param thresholds_keyword: Value of the keyword to be rendered. This must be a string representation of a dict, or a dict. :type thresholds_keyword: basestring, dict :returns: A table to be added into a cell in the keywords table. :rtype: safe.messaging.items.table """ if isinstance(thresholds_keyword, basestring): thresholds_keyword = literal_eval(thresholds_keyword) for k, v in thresholds_keyword.items(): # If the v is not dictionary, it should be the old value maps. # To handle thresholds in the Impact Function. if not isinstance(v, dict): table = m.Table(style_class='table table-condensed') for key, value in thresholds_keyword.items(): row = m.Row() name = definition(key)['name'] if definition(key) else key row.add(m.Cell(m.ImportantText(name))) pretty_value = tr('%s to %s' % (value[0], value[1])) row.add(m.Cell(pretty_value)) table.add(row) return table table = m.Table(style_class='table table-condensed table-striped') i = 0 for exposure_key, classifications in thresholds_keyword.items(): i += 1 exposure = definition(exposure_key) exposure_row = m.Row() exposure_row.add(m.Cell(m.ImportantText('Exposure'))) exposure_row.add(m.Cell(m.Text(exposure['name']))) exposure_row.add(m.Cell('')) table.add(exposure_row) active_classification = None classification_row = m.Row() classification_row.add(m.Cell(m.ImportantText('Classification'))) for classification, value in classifications.items(): if value.get('active'): active_classification = definition(classification) classification_row.add( m.Cell(active_classification['name'])) classification_row.add(m.Cell('')) break if not active_classification: classification_row.add(m.Cell(tr('No classifications set.'))) classification_row.add(m.Cell('')) continue table.add(classification_row) header = m.Row() header.add(m.Cell(tr('Class name'))) header.add(m.Cell(tr('Minimum'))) header.add(m.Cell(tr('Maximum'))) table.add(header) classes = active_classification.get('classes') # Sort by value, put the lowest first classes = sorted(classes, key=lambda k: k['value']) for the_class in classes: threshold = classifications[active_classification['key']][ 'classes'][the_class['key']] row = m.Row() row.add(m.Cell(the_class['name'])) row.add(m.Cell(threshold[0])) row.add(m.Cell(threshold[1])) table.add(row) if i < len(thresholds_keyword): # Empty row empty_row = m.Row() empty_row.add(m.Cell('')) empty_row.add(m.Cell('')) table.add(empty_row) return table
def _keyword_to_row(self, keyword, value, wrap_slash=False): """Helper to make a message row from a keyword. .. versionadded:: 3.2 Use this when constructing a table from keywords to display as part of a message object. :param keyword: The keyword to be rendered. :type keyword: str :param value: Value of the keyword to be rendered. :type value: basestring :param wrap_slash: Whether to replace slashes with the slash plus the html <wbr> tag which will help to e.g. wrap html in small cells if it contains a long filename. Disabled by default as it may cause side effects if the text contains html markup. :type wrap_slash: bool :returns: A row to be added to a messaging table. :rtype: safe.messaging.items.row.Row """ row = m.Row() # Translate titles explicitly if possible if keyword == 'title': value = tr(value) # # See #2569 if keyword == 'url': if isinstance(value, QUrl): value = value.toString() if keyword == 'date': if isinstance(value, QDateTime): value = value.toString('d MMM yyyy') elif isinstance(value, datetime): value = value.strftime('%d %b %Y') # we want to show the user the concept name rather than its key # if possible. TS keyword_definition = definition(keyword) if keyword_definition is None: keyword_definition = tr(keyword.capitalize().replace( '_', ' ')) else: try: keyword_definition = keyword_definition['name'] except KeyError: # Handling if name is not exist. keyword_definition = keyword_definition['key'].capitalize() keyword_definition = keyword_definition.replace('_', ' ') # We deal with some special cases first: # In this case the value contains a DICT that we want to present nicely if keyword in [ 'value_map', 'inasafe_fields', 'inasafe_default_values']: value = self._dict_to_row(value) elif keyword == 'value_maps': value = self._value_maps_row(value) elif keyword == 'thresholds': value = self._threshold_to_row(value) # In these KEYWORD cases we show the DESCRIPTION for # the VALUE keyword_definition elif keyword in ['classification']: # get the keyword_definition for this class from definitions value = definition(value) value = value['description'] # In these VALUE cases we show the DESCRIPTION for # the VALUE keyword_definition elif value in []: # get the keyword_definition for this class from definitions value = definition(value) value = value['description'] # In these VALUE cases we show the NAME for the VALUE # keyword_definition elif value in [ 'multiple_event', 'single_event', 'point', 'line', 'polygon' 'field']: # get the name for this class from definitions value = definition(value) value = value['name'] # otherwise just treat the keyword as literal text else: # Otherwise just directly read the value value = get_string(value) key = m.ImportantText(keyword_definition) row.add(m.Cell(key)) row.add(m.Cell(value, wrap_slash=wrap_slash)) return row
def to_message(self, keywords=None, show_header=True): """Format keywords as a message object. .. versionadded:: 3.2 .. versionchanged:: 3.3 - default keywords to None The message object can then be rendered to html, plain text etc. :param keywords: Keywords to be converted to a message. Optional. If not passed then we will attempt to get keywords from self.layer if it is not None. :type keywords: dict :param show_header: Flag indicating if InaSAFE logo etc. should be added above the keywords table. Default is True. :type show_header: bool :returns: A safe message object containing a table. :rtype: safe.messaging.Message """ if keywords is None and self.layer is not None: keywords = self.read_keywords(self.layer) # This order was determined in issue #2313 preferred_order = [ 'title', 'layer_purpose', 'exposure', 'hazard', 'hazard_category', 'layer_geometry', 'layer_mode', 'classification', 'exposure_unit', 'continuous_hazard_unit', 'value_map', # attribute values 'thresholds', # attribute values 'value_maps', # attribute values 'inasafe_fields', 'inasafe_default_values', 'resample', 'source', 'url', 'scale', 'license', 'date', 'keyword_version' ] # everything else in arbitrary order report = m.Message() if show_header: logo_element = m.Brand() report.add(logo_element) report.add(m.Heading(tr( 'Layer keywords:'), **styles.BLUE_LEVEL_4_STYLE)) report.add(m.Text(tr( 'The following keywords are defined for the active layer:'))) table = m.Table(style_class='table table-condensed table-striped') # First render out the preferred order keywords for keyword in preferred_order: if keyword in keywords: value = keywords[keyword] row = self._keyword_to_row(keyword, value) keywords.pop(keyword) table.add(row) # now render out any remaining keywords in arbitrary order for keyword in keywords: value = keywords[keyword] row = self._keyword_to_row(keyword, value) table.add(row) # If the keywords class was instantiated with a layer object # we can add some context info not stored in the keywords themselves # but that is still useful to see... if self.layer: # First the CRS keyword = tr('Reference system') value = self.layer.crs().authid() row = self._keyword_to_row(keyword, value) table.add(row) # Next the data source keyword = tr('Layer source') value = self.layer.source() row = self._keyword_to_row(keyword, value, wrap_slash=True) table.add(row) # Finalise the report report.add(table) return report
__copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'bd00bfeac510722b427544b186bfa10861749e51' ## # For QGIS < 2.18.13 and QGIS < 2.14.19, docstrings are used in the QGIS GUI # in the Expression dialog and also in the InaSAFE Help dialog. # # For QGIS >= 2.18.13, QGIS >= 2.14.19 and QGIS 3, the translated variable will # be used in QGIS. # help_text is used for QGIS 2.18 and 2.14 # helpText is used for QGIS 3 : https://github.com/qgis/QGIS/pull/5059 ## description = tr( 'Retrieve a value from a field in the analysis summary layer.') examples = { 'inasafe_analysis_summary_field_value(\'total_not_exposed\')': 3 } help_message = generate_expression_help(description, examples) @qgsfunction( args='auto', group='InaSAFE', usesGeometry=False, referencedColumns=[], help_text=help_message.to_html(), helpText=help_message.to_html()) def inasafe_analysis_summary_field_value(field, feature, parent): """Retrieve a value from a field in the analysis summary layer. e.g. inasafe_analysis_summary_field_value('total_not_exposed') -> 3 """ _ = feature, parent # NOQA
def action_checklist(self): """Polygon Population action. :returns: The population breakdown report. :rtype: safe.messaging.Message """ message = m.Message(style_class='container') message.add(m.Heading(tr('Action checklist'), **styles.INFO_STYLE)) population = population_rounding(sum( self.affected_population.values())) checklist = m.BulletedList() checklist.add(tr('Which group or people is most affected?')) checklist.add( tr('Who are the vulnerable people in the population and why?')) checklist.add(tr('How will warnings be disseminated?')) checklist.add(tr('What are people\'s likely movements?')) checklist.add( tr('What are the security factors for the affected people?')) checklist.add( tr('What are the security factors for relief responders?')) checklist.add(tr('How will we reach evacuated people?')) checklist.add( tr('What kind of food does the people normally consume?')) checklist.add( tr('What are the critical non-food items required by the affected ' 'people?')) checklist.add( tr('Are there enough water supply, sanitation, hygiene, food, ' 'shelter, medicines and relief items available for %s people?' % format_int(population))) checklist.add( tr('If yes, where are they located and how will we distribute them?' )) checklist.add( tr('If no, where can we obtain additional relief items and how will ' 'we distribute them?')) checklist.add(tr('What are the related health risks?')) checklist.add( tr('Who are the key people responsible for coordination?')) message.add(checklist) return message
# coding=utf-8 """Wizard Strings.""" from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'e96e916695a1f1d3ab34117194b58dc73d7743a4' category_question = tr( 'By following the simple steps in this wizard, you can assign ' 'keywords to your layer: <b>%s</b>. First you need to define the purpose ' 'of your layer. Is it a <b>hazard</b>, <b>exposure</b>, or ' '<b>aggregation</b> layer? ') # (layer name) category_question_hazard = tr( 'You have selected a layer that needs to have keywords assigned or ' 'updated. In the next steps you can assign keywords to that layer. ' 'First you need to confirm the layer is a <b>hazard</b> layer.') category_question_exposure = tr( 'You have selected a layer that needs to have keywords assigned or ' 'updated. In the next steps you can assign keywords to that layer. ' 'First you need to confirm the layer is an <b>exposure</b>.') category_question_aggregation = tr( 'You have selected a layer that needs to have keywords assigned or ' 'updated. In the next steps you can assign keywords to that layer. ' 'First you need to confirm the layer is an <b>aggregation</b> layer.') hazard_category_question = tr( 'What type of <b>hazard scenario</b> does this layer represent? ' '<p>Does it represent a <b>single event</b> or <b>multiple events</b>?' '</p>')
message = tr('No keywords file found for %s' % keyword_file_path) raise NoKeywordsFoundError(message) # now get the requested keyword using the inasafe library try: dictionary = read_keywords(keyword_file_path) except Exception, e: message = tr('Keyword retrieval failed for %s (%s) \n %s' % (keyword_file_path, keyword, str(e))) raise KeywordNotFoundError(message) # if no keyword was supplied, just return the dict if keyword is None: return dictionary if keyword not in dictionary: message = tr('No value was found in file %s for keyword %s' % (keyword_file_path, keyword)) raise KeywordNotFoundError(message) try: value = dictionary[keyword] except: raise return value def write_keywords_to_file(filename, keywords): """Thin wrapper around the safe write_keywords function. :param filename: Path to layer that must be written. If the file does not end in .keywords, its extension will be stripped off and the basename + .keywords will be used as the file.
def run_single_post_processor(layer, post_processor): """Run single post processor. If the layer has the output field, it will pass the post processor calculation. :param layer: The vector layer to use for post processing. :type layer: QgsVectorLayer :param post_processor: A post processor definition. :type post_processor: dict :returns: Tuple with True if success, else False with an error message. :rtype: (bool, str) """ if not layer.editBuffer(): # Turn on the editing mode. if not layer.startEditing(): msg = tr('The impact layer could not start the editing mode.') return False, msg # Calculate based on formula # Iterate all possible output and create the correct field. for output_key, output_value in post_processor['output'].items(): # Get output attribute name key = output_value['value']['key'] output_field_name = output_value['value']['field_name'] layer.keywords['inasafe_fields'][key] = output_field_name # If there is already the output field, don't proceed if layer.fieldNameIndex(output_field_name) > -1: msg = tr('The field name %s already exists.' % output_field_name) layer.rollBack() return False, msg # Add output attribute name to the layer field = create_field_from_definition(output_value['value']) result = layer.addAttribute(field) if not result: msg = tr('Error while creating the field %s.' % output_field_name) layer.rollBack() return False, msg # Get the index of output attribute output_field_index = layer.fieldNameIndex(output_field_name) if layer.fieldNameIndex(output_field_name) == -1: msg = tr('The field name %s has not been created.' % output_field_name) layer.rollBack() return False, msg # Get the input field's indexes for input input_indexes = {} input_properties = {} # Default parameters default_parameters = {} msg = None # Iterate over every inputs. for key, values in post_processor['input'].items(): values = values if isinstance(values, list) else [values] for value in values: is_constant_input = (value['type'] == constant_input_type) is_field_input = (value['type'] == field_input_type or value['type'] == dynamic_field_input_type) is_geometry_input = ( value['type'] == geometry_property_input_type) is_keyword_input = (value['type'] == keyword_input_type) is_needs_input = (value['type'] == needs_profile_input_type) is_layer_property_input = ( value['type'] == layer_property_input_type) if value['type'] == keyword_value_expected: break if is_constant_input: default_parameters[key] = value['value'] break elif is_field_input: if value['type'] == dynamic_field_input_type: key_template = value['value']['key'] field_param = value['field_param'] field_key = key_template % field_param else: field_key = value['value']['key'] inasafe_fields = layer.keywords['inasafe_fields'] name_field = inasafe_fields.get(field_key) if not name_field: msg = tr('%s has not been found in inasafe fields.' % value['value']['key']) continue index = layer.fieldNameIndex(name_field) if index == -1: fields = layer.fields().toList() msg = tr('The field name %s has not been found in %s' % (name_field, [f.name() for f in fields])) continue input_indexes[key] = index break # For geometry, create new field that contain the value elif is_geometry_input: input_properties[key] = geometry_property_input_type['key'] break # for keyword elif is_keyword_input: # See http://stackoverflow.com/questions/14692690/ # access-python-nested-dictionary-items-via-a-list-of-keys value = reduce(lambda d, k: d[k], value['value'], layer.keywords) default_parameters[key] = value break # for needs profile elif is_needs_input: need_parameter = minimum_needs_parameter( parameter_name=value['value']) value = need_parameter.value default_parameters[key] = value break # for layer property elif is_layer_property_input: if value['value'] == layer_crs_input_value: default_parameters[key] = layer.crs() if value['value'] == size_calculator_input_value: exposure = layer.keywords.get('exposure') if not exposure: keywords = layer.keywords.get('exposure_keywords') exposure = keywords.get('exposure') default_parameters[key] = SizeCalculator( layer.crs(), layer.geometryType(), exposure) break else: # executed when we can't find all the inputs layer.rollBack() return False, msg # Create iterator for feature request = QgsFeatureRequest().setSubsetOfAttributes( input_indexes.values()) iterator = layer.getFeatures(request) inputs = input_indexes.copy() inputs.update(input_properties) # Iterate all feature for feature in iterator: attributes = feature.attributes() # Create dictionary to store the input parameters = {} parameters.update(default_parameters) # Fill up the input from fields for key, value in inputs.items(): if value == geometry_property_input_type['key']: parameters[key] = feature.geometry() else: parameters[key] = attributes[value] # Fill up the input from geometry property # Evaluate the function python_function = output_value.get('function') if python_function: # Launch the python function post_processor_result = python_function(**parameters) else: # Evaluate the function formula = output_value['formula'] post_processor_result = evaluate_formula(formula, parameters) # The affected postprocessor returns a boolean. if isinstance(post_processor_result, bool): post_processor_result = tr(unicode(post_processor_result)) layer.changeAttributeValue(feature.id(), output_field_index, post_processor_result) layer.commitChanges() return True, None
def populate_parameter(self): """Helper to setup the parameter widget.""" used_fields = [] self.parameters = [] for field in self.field_group.get('fields', []): selected_option = DO_NOT_REPORT options = OrderedDict([ (DO_NOT_REPORT, { 'label': tr('Do not report'), 'value': None, 'type': STATIC, 'constraint': {} }), ]) # Example: count if field['absolute']: # Used in field options field_label = tr('Count fields') else: # Example: ratio # Used in field options field_label = tr('Ratio fields') global_default_value = get_inasafe_default_value_qsetting( self.setting, GLOBAL, field['key']) options[GLOBAL_DEFAULT] = { 'label': tr('Global default'), 'value': global_default_value, 'type': STATIC, 'constraint': {} } default_custom_value = get_inasafe_default_value_qsetting( self.setting, RECENT, field['key']) custom_value = self.metadata.get('inasafe_default_values', {}).get( field['key'], default_custom_value) if field['key'] in self.metadata.get('inasafe_default_values', {}): if custom_value == global_default_value: selected_option = GLOBAL_DEFAULT else: selected_option = CUSTOM_VALUE min_value = field['default_value'].get('min_value', 0) max_value = field['default_value'].get('max_value', 100) default_step = (max_value - min_value) / 100.0 step = field['default_value'].get('increment', default_step) options[CUSTOM_VALUE] = { 'label': tr('Custom'), 'value': custom_value, 'type': SINGLE_DYNAMIC, 'constraint': { 'min': min_value, 'max': max_value, 'step': step } } custom_fields = self.metadata.get('inasafe_fields', {}).get(field['key'], []) if field['key'] in self.metadata.get('inasafe_fields', {}): selected_option = FIELDS if isinstance(custom_fields, str): custom_fields = [custom_fields] options[FIELDS] = { 'label': field_label, 'value': custom_fields, 'type': MULTIPLE_DYNAMIC, 'constraint': {} } used_fields.extend(custom_fields) parameter = GroupSelectParameter() parameter.guid = field['key'] parameter.name = field['name'] parameter.options = options parameter.selected = selected_option parameter.help_text = field['help_text'] parameter.description = field['description'] self.parameters.append(parameter) self.parameter_container = ParameterContainer( parameters=self.parameters, extra_parameters=self.extra_parameters, vertical=False) self.parameter_container.setup_ui() constraints = self.field_group.get('constraints', {}) for key, value in list(constraints.items()): self.parameter_container.add_validator( validators[key], kwargs=value['kwargs'], validation_message=value['message']) self.parameter_layout.addWidget(self.parameter_container) default_ratio_help_text = tr( 'By default, InaSAFE will calculate the default ratio ' 'however users have the option to include this in the ' 'analysis report. If you do not want to see the default ' 'results in the report choose "do not report".') # Set move or copy if self.field_group.get('exclusive', False): # If exclusive, do not add used field. self.populate_field_list(excluded_fields=used_fields) # Use move action since it's exclusive self.field_list.setDefaultDropAction(Qt.MoveAction) # Just make sure that the signal is disconnected try: # noinspection PyUnresolvedReferences self.field_list.itemChanged.disconnect(self.drop_remove) except TypeError: pass # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can only map one field to one concept.') else: # If not exclusive, add all field. self.populate_field_list() # Use copy action since it's not exclusive self.field_list.setDefaultDropAction(Qt.CopyAction) # noinspection PyUnresolvedReferences self.field_list.itemChanged.connect( partial(self.drop_remove, field_list=self.field_list)) self.connect_drop_remove_parameter() # Set header header_text = self.field_group['description'] header_text += '\n\n' + default_ratio_help_text header_text += '\n\n' + tr( 'You can map one field to more than one concepts.') self.header_label.setText(header_text)
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 = tr(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 = tr(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
# coding=utf-8 """Definitions relating to default value.""" from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'bc3796555162c60f37041c9b01cea5ad77504d39' female_ratio_default_value = { 'key': 'female_ratio_default_value', 'name': tr('Female Ratio Global Default'), # https://www.cia.gov/library/publications/the-world-factbook/geos/xx.html # total population: 1.01 male(s)/female (2011 est.) 'default_value': 0.496, # Updated for InaSAFE 4.1 # UNSD World Data, 2010. 'min_value': 0, 'max_value': 1, 'increment': 0.001, 'description': tr('Default ratio of females per 100 people in the total population.') }
def __init__(self, field_group=None, parent=None, iface=None): """Constructor.""" # Init from parent class QWidget.__init__(self, parent) # Attributes self.layer = None self.metadata = {} self.parent = parent self.iface = iface self.field_group = field_group self.setting = QSettings() # TODO(IS): Make dynamic # Main container self.main_layout = QVBoxLayout() # Inner layout self.header_layout = QHBoxLayout() self.content_layout = QHBoxLayout() self.footer_layout = QHBoxLayout() # Header self.header_label = QLabel() self.header_label.setWordWrap(True) # Content self.field_layout = QVBoxLayout() self.parameter_layout = QHBoxLayout() self.field_description = QLabel(tr('List of fields')) self.field_list = QListWidget() self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.field_list.setDragDropMode(QAbstractItemView.DragDrop) self.field_list.setDefaultDropAction(Qt.MoveAction) self.field_list.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding) # noinspection PyUnresolvedReferences self.field_list.itemSelectionChanged.connect(self.update_footer) # Footer self.footer_label = QLabel() self.footer_label.setWordWrap(True) # Parameters self.extra_parameters = [(GroupSelectParameter, GroupSelectParameterWidget)] self.parameters = [] self.parameter_container = None # Adding to layout self.header_layout.addWidget(self.header_label) self.field_layout.addWidget(self.field_description) self.field_layout.addWidget(self.field_list) self.field_layout.setSizeConstraint(QLayout.SetMaximumSize) self.content_layout.addLayout(self.field_layout) self.content_layout.addLayout(self.parameter_layout) self.footer_layout.addWidget(self.footer_label) self.main_layout.addLayout(self.header_layout) self.main_layout.addLayout(self.content_layout) self.main_layout.addLayout(self.footer_layout) self.setLayout(self.main_layout)
__email__ = "*****@*****.**" __revision__ = '8008783db0ca72c7a950460515728c12b5977d8d' # Individual report component analysis_question_component = { 'key': 'analysis-question', 'type': jinja2_component_type, 'processor': jinja2_renderer, 'extractor': analysis_question_extractor, 'output_format': Jinja2ComponentsMetadata.OutputFormat.String, 'output_path': 'analysis-result-output.html', 'template': 'standard-template/' 'jinja2/' 'analysis-question.html', 'extra_args': { 'header': tr('Analysis question') } } general_report_component = { 'key': 'general-report', 'type': jinja2_component_type, 'processor': jinja2_renderer, 'extractor': general_report_extractor, 'output_format': Jinja2ComponentsMetadata.OutputFormat.String, 'output_path': 'general-report-output.html', 'template': 'standard-template/' 'jinja2/' 'general-report.html', 'extra_args': { 'header':
# coding=utf-8 """Caveats and advisories that will be shown in reports. Some of these may be shown only in certain contexts. """ from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'fdf1afffab4271e5fbb873566278d5b4a7ff5722' caveat_simulation = tr( 'The extent and severity of the mapped scenario or hazard zones ' 'may not be consistent with future events.') caveat_local_conditions = tr( 'The impacts on roads, people, buildings and other exposure ' 'elements may differ from the analysis results due to local ' 'conditions such as terrain and infrastructure type.') caveat_incomplete_data = tr( 'The impacts on roads, people, buildings and other exposure ' 'elements may be underestimated if the exposure data are incomplete.') caveat_analysis_extent = tr( 'The analysis extent is limited to the extent of the aggregation ' 'layer or analysis extent. Hazard and exposure data outside the analysis ' 'extent are not included in the impact layer, impact map or impact ' 'reports.') caveat_analysis_ommission = tr( 'Structures overlapping the analysis extent may be assigned a hazard ' 'status lower than that to which they are exposed outside the analysis '
count_ratio_mapping[count_fields[index] ['key']] = ratio_fields[index]['key'] all_field_groups = [ age_ratio_group, age_count_group, age_displaced_count_group, gender_ratio_group, gender_count_group, gender_displaced_count_group, disability_vulnerability_ratio_group, disability_vulnerability_count_group, disability_vulnerability_displaced_count_group, gender_vulnerability_ratio_group, gender_vulnerability_count_group, gender_vulnerability_displaced_count_group, age_vulnerability_ratio_group, age_vulnerability_count_group, age_vulnerability_displaced_count_group ] # Update notes for each group age_group_notes = [ tr('Infant: {note}').format(note=concepts['infant']['description']), tr('Child: {note}').format(note=concepts['child']['description']), tr('Youth: {note}').format(note=concepts['youth']['description']), tr('Adult: {note}').format(note=concepts['adult']['description']), tr('Elderly: {note}').format(note=concepts['elderly']['description']) ] gender_group_notes = [ tr('Male: {note}').format(note=concepts['male']['description']), tr('Female: {note}').format(note=concepts['female']['description']), ] age_vulnerability_group_notes = [ tr('Under 5: {note}').format(note=concepts['under_5']['description']), tr('Over 60: {note}').format(note=concepts['over_60']['description']), ]
def content(): """Helper method that returns just the content. This method was added so that the text could be reused in the dock_help module. .. versionadded:: 4.1.0 :returns: A message object without brand element. :rtype: safe.messaging.message.Message """ message = m.Message() message.add( m.Paragraph( tr('This section of the help documentation is intended for advanced ' 'users who want to modify the internals of InaSAFE. It assumes that ' 'you have basic coding skills. All examples are in python unless ' 'otherwise stated.'))) message.add(m.Heading(tr('Defining a new hazard type'), **SUBSECTION_STYLE)) message.add(m.Heading(tr('Background'), **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph( tr('In the previous versions of InaSAFE, we spent a lot of effort ' 'building one impact function per hazard/exposure combination (and ' 'sometimes multiple impact functions per combination). In our new ' 'architecture, we try to deal with everything in the same way - by ' 'following a standardized process of converting the hazard dataset ' 'into a classified polygon layer and then calculating the impacted ' 'and affected areas using a standard work-flow. A simplified version ' 'of this work-flow is described in illustration 1.')) message.add(paragraph) paragraph = m.Paragraph( tr('Because of this change, you will no longer see an impact function ' 'selector in the dock widget and there are no longer any \'impact ' 'function options\' as we had in previous versions of InaSAFE. In ' 'the new system, almost all configuration is managed through ' 'metadata (created using the keywords wizard).')) message.add(paragraph) paragraph = m.Paragraph( tr('Also, in all versions prior to Version 4.0, we made heavy use of ' 'interpolation in order to determine whether buildings or other ' 'exposure layers are impacted. While this is a commonly used ' 'technique in GIS, it often leads to non - intuitive looking ' 'reports. Under our new architecture, we always use geometric ' 'overlay operations to make a determination whether an exposure ' 'feature is affected or not. The implication of this is that we ' 'produce intuitive and easily verifiable impact layers. You can ' 'see an example in Illustration 2.')) message.add(paragraph) paragraph = m.Paragraph( tr('Stepping away from the two previously mentioned paradigms allows ' 'us to simply add new hazard types to the metadata driven impact ' 'function by adding new metadata types to the InaSAFE sources. ' 'In the next chapter we show you how this was achieved and how ' 'it can be repeated for further hazards using the example of ' 'tropical cyclones.')) message.add(paragraph) message.add(m.Heading(tr('Adding a new hazard'), **BLUE_CHAPTER_STYLE)) link = m.Link('https://github.com/inasafe/inasafe/pull/3539/files', tr('Pull Request #3539')) paragraph = m.Paragraph( tr('The whole work needed can be looked at in '), link, tr('. Please bear in mind that the paths of the files are now ' 'safe/definitions/xxx.py and not safe/definitionsv4/xxx.py since ' 'v4 is the default codebase. In the next sections we will show ' 'each file that needs to be extended in order to add a new hazard ' 'type.')) message.add(paragraph) # Setting up units message.add( m.Heading(tr('safe/definitions/units.py'), **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph( tr('If you are adding an hazard that uses units that are not yet known ' 'to InaSAFE, you need to define them in units.py')) message.add(paragraph) paragraph = m.PreformattedText( _get_definition_from_module(units, 'unit_kilometres_per_hour')) message.add(paragraph) # Setting up style message.add(m.Heading('safe/definitions/colors.py', **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph( 'If you are adding an hazard that has more classes than any other ' 'hazards you’ll need to add additional colors for the additional ' 'classes in colors.py. You might also define other colors if you ' 'don\'t want to use the standard colors. For the sake of homogeneous ' 'map reports, this addition should not be taken lightly.') message.add(paragraph) # Don't translate this paragraph = m.PreformattedText('very_dark_red = Qcolor(\'#710017\')') message.add(paragraph) # Setting up hazard classification message.add( m.Heading('safe/definitions/hazard_classifications.py', **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph( tr('Add the classifications you want to make available for your new ' 'hazard type. You can add as many classes as you want in the ' 'classes list.')) message.add(paragraph) paragraph = m.Paragraph( tr('Also, a classification can support multiple units so you don\'t ' 'have to define different classifications just to have the same ' 'classification in two or more different units. These are defined ' 'in the multiple_units attribute of the classification.')) message.add(paragraph) paragraph = m.PreformattedText( _get_definition_from_module(hazard_classifications, 'cyclone_au_bom_hazard_classes')) message.add(paragraph) # Setting up wizard questions message.add( m.Heading('safe/gui/tools/wizard/wizard_strings.py', **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph(tr('Define the questions for the wizard:')) message.add(paragraph) # don not translate message.add( m.PreformattedText( 'cyclone_kilometres_per_hour_question = tr(\'wind speed in km/h\')' )) message.add( m.PreformattedText( 'cyclone_miles_per_hour_question = tr(\'wind speed in mph\')')) message.add( m.PreformattedText( 'cyclone_knots_question = tr(\'wind speed in kn\')')) # Setting up message.add(m.Heading('safe/definitions/hazard.py', **BLUE_CHAPTER_STYLE)) paragraph = m.Paragraph( tr('Finally define new hazard and add it to the hazard_all list:')) message.add(paragraph) paragraph = m.PreformattedText( _get_definition_from_module(hazard, 'hazard_cyclone')) message.add(paragraph) paragraph = m.Paragraph( tr('Finally define new hazard and add it to the hazard_all list:')) message.add(paragraph) paragraph = m.PreformattedText( _get_definition_from_module(hazard, 'hazard_all')) message.add(paragraph) return message
def clip(layer_to_clip, mask_layer, callback=None): """Clip a vector layer with another. Issue https://github.com/inasafe/inasafe/issues/3186 Note : This algorithm is copied from : https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/ qgis/Clip.py :param layer_to_clip: The vector layer to clip. :type layer_to_clip: QgsVectorLayer :param mask_layer: The vector layer to use for clipping. :type mask_layer: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The clip vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = clip_steps['output_layer_name'] output_layer_name = output_layer_name % ( layer_to_clip.keywords['layer_purpose']) processing_step = clip_steps['step_name'] writer = create_memory_layer(output_layer_name, layer_to_clip.geometryType(), layer_to_clip.crs(), layer_to_clip.fields()) writer.startEditing() # Begin copy/paste from Processing plugin. # Please follow their code as their code is optimized. # The code below is not following our coding standards because we want to # be able to track any diffs from QGIS easily. # first build up a list of clip geometries clip_geometries = [] request = QgsFeatureRequest().setSubsetOfAttributes([]) for mask_feature in mask_layer.getFeatures(request): clip_geometries.append(QgsGeometry(mask_feature.geometry())) # are we clipping against a single feature? if so, # we can show finer progress reports if len(clip_geometries) > 1: # noinspection PyTypeChecker,PyCallByClass,PyArgumentList combined_clip_geom = QgsGeometry.unaryUnion(clip_geometries) single_clip_feature = False else: combined_clip_geom = clip_geometries[0] single_clip_feature = True # use prepared geometries for faster intersection tests # noinspection PyArgumentList engine = QgsGeometry.createGeometryEngine(combined_clip_geom.geometry()) engine.prepareGeometry() tested_feature_ids = set() for i, clip_geom in enumerate(clip_geometries): request = QgsFeatureRequest().setFilterRect(clip_geom.boundingBox()) input_features = [f for f in layer_to_clip.getFeatures(request)] if not input_features: continue if single_clip_feature: total = 100.0 / len(input_features) else: total = 0 for current, in_feat in enumerate(input_features): if not in_feat.geometry(): continue if in_feat.id() in tested_feature_ids: # don't retest a feature we have already checked continue tested_feature_ids.add(in_feat.id()) if not engine.intersects(in_feat.geometry().geometry()): continue if not engine.contains(in_feat.geometry().geometry()): cur_geom = in_feat.geometry() new_geom = combined_clip_geom.intersection(cur_geom) if new_geom.wkbType() == QgsWKBTypes.Unknown \ or QgsWKBTypes.flatType( new_geom.geometry().wkbType()) == \ QgsWKBTypes.GeometryCollection: int_com = in_feat.geometry().combine(new_geom) int_sym = in_feat.geometry().symDifference(new_geom) if not int_com or not int_sym: # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass else: new_geom = int_com.difference(int_sym) if new_geom.isGeosEmpty()\ or not new_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more ' # 'input features have invalid geometry.')) pass else: # clip geometry totally contains feature geometry, # so no need to perform intersection new_geom = in_feat.geometry() try: out_feat = QgsFeature() out_feat.setGeometry(new_geom) out_feat.setAttributes(in_feat.attributes()) if new_geom.type() == layer_to_clip.geometryType(): writer.addFeature(out_feat) except: LOGGER.debug( tr('Feature geometry error: One or more output features ' 'ignored due to invalid geometry.')) continue # TODO implement callback if single_clip_feature: # progress.setPercentage(int(current * total)) pass if not single_clip_feature: # coarse progress report for multiple clip geometries # progress.setPercentage(100.0 * i / len(clip_geoms)) pass # End copy/paste from Processing plugin. writer.commitChanges() writer.keywords = layer_to_clip.keywords.copy() writer.keywords['title'] = output_layer_name check_layer(writer) return writer
# coding=utf-8 """This module contains constants that are used in definitions.""" from PyQt4.QtCore import QVariant from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = '8ecf2f4ed59dc0518a3013c08b8f4f865bbfe1d7' inasafe_keyword_version_key = 'keyword_version' no_field = tr('No Field') no_data_value = 200 zero_default_value = 0.0 big_number = 9999 # Whole Number in QVariant / Integer as in math qvariant_whole_numbers = [ QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong ] # Number in QVariant qvariant_numbers = qvariant_whole_numbers + [ QVariant.Double, ] qvariant_all = qvariant_numbers + [QVariant.String]
from safe.utilities.i18n import tr __copyright__ = "Copyright 2017, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'fdf1afffab4271e5fbb873566278d5b4a7ff5722' EXTRA_CALORIES_NEEDED_PER_DAY = 500 # in KKal / day DAY_IN_A_WEEK = 7 # in day / week KG_RICE_PER_CALORIES = 0.1 / 129 # in KKal (100 gram gives 129 KKal calories) post_processor_additional_rice = { 'key': 'post_processor_additional_rice', 'name': tr('Additional Weekly Rice kg for Pregnant and Lactating Women Post ' 'Processor'), 'description': tr('A post processor to calculate additional rice for pregnant and ' 'lactating women who are displaced. ' '"Displaced" is defined as: {displaced_concept}').format( displaced_concept=concepts['displaced_people']['description']), 'input': { 'pregnant_displaced': [{ 'value': pregnant_displaced_count_field, 'type': field_input_type, }], 'lactating_displaced': [{ 'value': lactating_displaced_count_field, 'type': field_input_type, }], 'additional_rice_ratio': {
def minimum_needs_extractor(impact_report, component_metadata): """Extracting minimum needs of the impact layer. :param impact_report: the impact report that acts as a proxy to fetch all the data that extractor needed :type impact_report: safe.report.impact_report.ImpactReport :param component_metadata: the component metadata. Used to obtain information about the component we want to render :type component_metadata: safe.report.report_metadata. ReportComponentsMetadata :return: context for rendering phase :rtype: dict .. versionadded:: 4.0 """ context = {} extra_args = component_metadata.extra_args analysis_layer = impact_report.analysis analysis_keywords = analysis_layer.keywords['inasafe_fields'] use_rounding = impact_report.impact_function.use_rounding header = resolve_from_dictionary(extra_args, 'header') context['header'] = header # check if displaced is not zero try: displaced_field_name = analysis_keywords[displaced_field['key']] total_displaced = value_from_field_name(displaced_field_name, analysis_layer) if total_displaced == 0: zero_displaced_message = resolve_from_dictionary( extra_args, 'zero_displaced_message') context['zero_displaced'] = { 'status': True, 'message': zero_displaced_message } return context except KeyError: # in case no displaced field pass # minimum needs calculation only affect population type exposure # check if analysis keyword have minimum_needs keywords have_minimum_needs_field = False for field_key in analysis_keywords: if field_key.startswith(minimum_needs_namespace): have_minimum_needs_field = True break if not have_minimum_needs_field: return context frequencies = {} # map each needs to its frequency groups for field in (minimum_needs_fields + additional_minimum_needs): need_parameter = field.get('need_parameter') if isinstance(need_parameter, ResourceParameter): frequency = need_parameter.frequency else: frequency = field.get('frequency') if frequency: if frequency not in frequencies: frequencies[frequency] = [field] else: frequencies[frequency].append(field) needs = [] analysis_feature = next(analysis_layer.getFeatures()) header_frequency_format = resolve_from_dictionary( extra_args, 'header_frequency_format') total_header = resolve_from_dictionary(extra_args, 'total_header') need_header_format = resolve_from_dictionary(extra_args, 'need_header_format') # group the needs by frequency for key, frequency in list(frequencies.items()): group = { 'header': header_frequency_format.format(frequency=tr(key)), 'total_header': total_header, 'needs': [] } for field in frequency: # check value exists in the field field_idx = analysis_layer.fields().lookupField( field['field_name']) if field_idx == -1: # skip if field doesn't exists continue value = format_number(analysis_feature[field_idx], use_rounding=use_rounding, is_population=True) unit_abbreviation = '' if field.get('need_parameter'): need_parameter = field['need_parameter'] """:type: ResourceParameter""" name = tr(need_parameter.name) unit_abbreviation = need_parameter.unit.abbreviation else: if field.get('header_name'): name = field.get('header_name') else: name = field.get('name') need_unit = field.get('unit') if need_unit: unit_abbreviation = need_unit.get('abbreviation') if unit_abbreviation: header = need_header_format.format( name=name, unit_abbreviation=unit_abbreviation) else: header = name item = {'header': header, 'value': value} group['needs'].append(item) needs.append(group) context['component_key'] = component_metadata.key context['needs'] = needs return context
# coding=utf-8 """ Definitions relating to layer modes (continuous or classified data). """ from safe.utilities.i18n import tr __copyright__ = "Copyright 2016, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'b68f9f9b473d275ea4d63c7b831184f2c08e0e6e' layer_mode_continuous = { 'key': 'continuous', 'name': tr('Continuous'), 'description': tr( '<b>Continuous</b> data can be used in raster hazard or exposure data ' 'where the values in the data are either integers or decimal values ' 'representing a continuously varying phenomenon. For example flood ' 'depth is a continuous value from 0 to the maximum reported depth ' 'during a flood. <p>Raster exposure data such as population data are ' 'also continuous. In this example the cell values represent the ' 'number of people in cell.</p>' '<p>Raster data is considered to be continuous by default and you ' 'should explicitly indicate that it is classified if each cell in the ' 'raster represents a discrete class (e.g. low depth = 1, medium depth ' '= 2, high depth = 3).</p>' ), 'citations': [ {
def build_widget(self, form_layout, name, key_value): """Create a new form element dynamically based from theValue type. The element will be inserted to theFormLayout. :param form_layout: Mandatory a layout instance :type form_layout: QFormLayout :param name: Mandatory string referencing the key in the function configurable parameters dictionary. :type name: str :param key_value: Mandatory representing the value referenced by the key. :type key_value: object :returns: a function that return the value of widget :raises: None """ # create label if isinstance(name, str): label = QLabel() label.setObjectName(_fromUtf8(name + "Label")) label_text = name.replace('_', ' ').capitalize() label.setText(tr(label_text)) label.setToolTip(str(type(key_value))) else: label = name # create widget based on the type of key_value variable # if widget is a QLineEdit, value needs to be set # if widget is NOT a QLineEdit, property_name needs to be set value = None property_name = None # can be used for widgets that have their own text like QCheckBox hide_label = False if isinstance(key_value, list): def function(values_string): """ :param values_string: This contains the list of values the user added to the line edit for the parameter. :type values_string: basestring :returns: list of value types :rtype: list """ value_type = type(key_value[0]) return [value_type(y) for y in str(values_string).split(',')] widget = QLineEdit() value = ', '.join([str(x) for x in key_value]) # NOTE: we assume that all element in list have same type elif isinstance(key_value, dict): def function(key_values_string): """ :param key_values_string: This contains the dictionary that the used defined on the line edit for this parameter. :type key_values_string: basestring :returns: a safe evaluation of the dict :rtype: dict """ return ast.literal_eval(str(key_values_string)) widget = QLineEdit() value = str(key_value) elif isinstance(key_value, bool): function = bool widget = QCheckBox() widget.setChecked(key_value) widget.setText(label.text()) property_name = 'checked' hide_label = True else: function = type(key_value) widget = QLineEdit() value = str(key_value) if hide_label: form_layout.addRow(widget) else: form_layout.addRow(label, widget) if isinstance(widget, QLineEdit): widget.setText(value) property_name = 'text' return self.bind(widget, property_name, function)
from safe.definitions.fields import (child_bearing_age_displaced_count_field, pregnant_displaced_count_field, lactating_displaced_count_field) from safe.utilities.i18n import tr __copyright__ = "Copyright 2017, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = 'b68f9f9b473d275ea4d63c7b831184f2c08e0e6e' gender_vulnerability_ratio_group = { 'key': 'gender_vulnerability_ratio_group', 'name': tr('Gender Vulnerability Ratio'), 'description': tr('Demographic breakdown to use for displaced population based on ' 'gender. Vulnerability ratio groupings are used when there ' 'is a vector aggregation layer that contains detailed demographic ' 'information (as ratios) about the population living in each ' 'administrative or census area. These ratios are then applied to the ' 'count of displaced population per aggregation area to provide a more ' 'detailed break down of the number of people displaced in each ' 'gender profile. Vulnerable segments of the population ' 'can include criteria like the number of infants, the number of ' 'elderly, the number of disabled people, and so on.'), 'fields': [ child_bearing_age_ratio_field, pregnant_ratio_field, lactating_ratio_field ],
def as_dict(): """Return metadata as a dictionary. This is a static method. You can use it to get the metadata in dictionary format for an impact function. :returns: A dictionary representing all the metadata for the concrete impact function. :rtype: dict """ dict_meta = { 'id': 'FloodRasterBuildingFunction', 'name': tr('Raster flood on buildings'), 'impact': tr('Be flooded'), 'title': tr('Be flooded'), 'function_type': 'old-style', # should be a list, but we can do it later. 'author': 'Ole Nielsen and Kristy van Putten', 'date_implemented': 'N/A', 'overview': tr('To assess the impacts of (flood or tsunami) inundation ' 'on building footprints originating from OpenStreetMap ' '(OSM) with hazard in raster format.'), 'detailed_description': tr('The inundation status is calculated for each building ' '(using the centroid if it is a polygon) based on the ' 'flood threshold. The threshold can be configured in ' 'impact function options.'), 'hazard_input': tr('A hazard raster layer where each cell represents flood ' 'depth (in meters).'), 'exposure_input': tr('Vector polygon or point layer extracted from OSM where ' 'each feature represents the footprint of a building.'), 'output': tr('Vector layer contains building is estimated to be ' 'flooded and the breakdown of the building by type.'), 'actions': tr('Provide details about where critical infrastructure ' 'might be flooded.'), 'limitations': [ tr('This function only flags buildings as impacted or not ' 'either based on a fixed threshold') ], 'citations': [], 'layer_requirements': { 'hazard': { 'layer_mode': layer_mode_continuous, 'layer_geometries': [layer_geometry_raster], 'hazard_categories': [ hazard_category_single_event, hazard_category_multiple_event ], 'hazard_types': [hazard_flood], 'continuous_hazard_units': [unit_feet, unit_metres], 'vector_hazard_classifications': [], 'raster_hazard_classifications': [], 'additional_keywords': [] }, 'exposure': { 'layer_mode': layer_mode_classified, 'layer_geometries': [layer_geometry_point, layer_geometry_polygon], 'exposure_types': [exposure_structure], 'exposure_units': [], 'exposure_class_fields': [structure_class_field], 'additional_keywords': [] } }, 'parameters': OrderedDict([('threshold', threshold()), ('postprocessors', OrderedDict([('BuildingType', building_type_postprocessor())]))]) } return dict_meta
from safe.definitions.fields import (female_ratio_field, male_ratio_field, female_count_field, male_count_field, male_displaced_count_field, female_displaced_count_field) from safe.utilities.i18n import tr __copyright__ = "Copyright 2017, The InaSAFE Project" __license__ = "GPL version 3" __email__ = "*****@*****.**" __revision__ = '2f075ced569597e81df73adc9a7e1439daa99814' gender_ratio_group = { 'key': 'gender_ratio_group', 'name': tr('Gender Ratio'), 'description': tr('Demographic breakdown to use for displaced population based on ' 'gender. Gender ratio groupings are used when there is a vector ' 'aggregation layer that contains detailed demographic information ( ' 'as ratios) about the population living in each administrative or ' 'census area. These ratios are then applied to the count of displaced ' 'population per aggregation area to provide a more detailed break ' 'down of the number of people displaced in each gender profile. ' 'Gender specific info can include criteria like the number of ' 'females, the number of females of child bearing age, and so on.'), 'fields': [ male_ratio_field, female_ratio_field, ], # Exclusive = False: able to put same layer's field to some fields
def intersection(source, mask, callback=None): """Intersect two layers. Issue https://github.com/inasafe/inasafe/issues/3186 Note : This algorithm is copied from : https://github.com/qgis/QGIS/blob/master/python/plugins/processing/algs/ qgis/Intersection.py :param source: The vector layer to clip. :type source: QgsVectorLayer :param mask: The vector layer to use for clipping. :type mask: QgsVectorLayer :param callback: A function to all to indicate progress. The function should accept params 'current' (int), 'maximum' (int) and 'step' (str). Defaults to None. :type callback: function :return: The clip vector layer. :rtype: QgsVectorLayer .. versionadded:: 4.0 """ output_layer_name = intersection_steps['output_layer_name'] output_layer_name = output_layer_name % (source.keywords['layer_purpose']) processing_step = intersection_steps['step_name'] fields = source.fields() fields.extend(mask.fields()) writer = create_memory_layer(output_layer_name, source.geometryType(), source.crs(), fields) writer.startEditing() # Begin copy/paste from Processing plugin. # Please follow their code as their code is optimized. # The code below is not following our coding standards because we want to # be able to track any diffs from QGIS easily. out_feature = QgsFeature() index = create_spatial_index(mask) # Todo callback # total = 100.0 / len(selectionA) for current, in_feature in enumerate(source.getFeatures()): # progress.setPercentage(int(current * total)) geom = in_feature.geometry() attributes = in_feature.attributes() intersects = index.intersects(geom.boundingBox()) for i in intersects: request = QgsFeatureRequest().setFilterFid(i) feature_mask = next(mask.getFeatures(request)) tmp_geom = feature_mask.geometry() if geom.intersects(tmp_geom): mask_attributes = feature_mask.attributes() int_geom = QgsGeometry(geom.intersection(tmp_geom)) if int_geom.wkbType() == QgsWKBTypes.Unknown\ or QgsWKBTypes.flatType( int_geom.geometry().wkbType()) ==\ QgsWKBTypes.GeometryCollection: int_com = geom.combine(tmp_geom) int_geom = QgsGeometry() if int_com: int_sym = geom.symDifference(tmp_geom) int_geom = QgsGeometry(int_com.difference(int_sym)) if int_geom.isGeosEmpty() or not int_geom.isGeosValid(): # LOGGER.debug( # tr('GEOS geoprocessing error: One or more input ' # 'features have invalid geometry.')) pass try: geom_types = wkb_type_groups[wkb_type_groups[ int_geom.wkbType()]] if int_geom.wkbType() in geom_types: if int_geom.type() == source.geometryType(): # We got some features which have not the same # kind of geometry. We want to skip them. out_feature.setGeometry(int_geom) attrs = [] attrs.extend(attributes) attrs.extend(mask_attributes) out_feature.setAttributes(attrs) writer.addFeature(out_feature) except: LOGGER.debug( tr('Feature geometry error: One or more output ' 'features ignored due to invalid geometry.')) continue # End copy/paste from Processing plugin. writer.commitChanges() writer.keywords = dict(source.keywords) writer.keywords['title'] = output_layer_name writer.keywords['layer_purpose'] = layer_purpose_exposure_summary['key'] writer.keywords['inasafe_fields'] = dict(source.keywords['inasafe_fields']) writer.keywords['inasafe_fields'].update(mask.keywords['inasafe_fields']) writer.keywords['hazard_keywords'] = dict(mask.keywords['hazard_keywords']) writer.keywords['exposure_keywords'] = dict(source.keywords) writer.keywords['aggregation_keywords'] = dict( mask.keywords['aggregation_keywords']) check_layer(writer) return writer