def layer_changed(self, layer): """Enable or disable keywords editor icon when active layer changes. :param layer: The layer that is now active. :type layer: QgsMapLayer """ if not layer: enable_keyword_wizard = False elif not hasattr(layer, 'providerType'): enable_keyword_wizard = False elif layer.providerType() == 'wms': enable_keyword_wizard = False else: enable_keyword_wizard = True try: if layer: if is_raster_layer(layer): enable_field_mapping_tool = False else: keywords = KeywordIO().read_keywords(layer) keywords_version = keywords.get('keyword_version') if not keywords_version: supported = False else: supported = ( is_keyword_version_supported(keywords_version)) if not supported: enable_field_mapping_tool = False else: layer_purpose = keywords.get('layer_purpose') if not layer_purpose: enable_field_mapping_tool = False else: if layer_purpose == layer_purpose_exposure['key']: layer_subcategory = keywords.get('exposure') elif layer_purpose == layer_purpose_hazard['key']: layer_subcategory = keywords.get('hazard') else: layer_subcategory = None field_groups = get_field_groups( layer_purpose, layer_subcategory) if len(field_groups) == 0: # No field group, disable field mapping tool. enable_field_mapping_tool = False else: enable_field_mapping_tool = True else: enable_field_mapping_tool = False except (KeywordNotFoundError, NoKeywordsFoundError, MetadataReadError): # No keywords, disable field mapping tool. enable_field_mapping_tool = False self.action_keywords_wizard.setEnabled(enable_keyword_wizard) self.action_field_mapping.setEnabled(enable_field_mapping_tool)
def test_is_keyword_version_supported(self): """Test for is_keyword_version_supported.""" self.assertTrue(is_keyword_version_supported('3.2', '3.2')) self.assertTrue(is_keyword_version_supported('3.2', '3.3')) self.assertTrue(is_keyword_version_supported('3.2.1', '3.2')) self.assertTrue(is_keyword_version_supported('3.2.1-alpha', '3.2')) self.assertTrue(is_keyword_version_supported('3.2.1', '3.3')) self.assertFalse(is_keyword_version_supported('3.02.1', '3.2'))
def get_layer_description_from_canvas(self, layer, purpose): """Obtain the description of a canvas layer selected by user. :param layer: The QGIS layer. :type layer: QgsMapLayer :param purpose: The layer purpose of the layer to get the description. :type purpose: string :returns: description of the selected layer. :rtype: string """ if not layer: return "" try: keywords = self.keyword_io.read_keywords(layer) if 'layer_purpose' not in keywords: keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # set the current layer (e.g. for the keyword creation sub-thread) self.layer = layer if purpose == layer_purpose_hazard['key']: self.hazard_layer = layer elif purpose == layer_purpose_exposure['key']: self.exposure_layer = layer else: self.aggregation_layer = layer # Check if the layer is keywordless if keywords and 'keyword_version' in keywords: kw_ver = str(keywords['keyword_version']) self.is_selected_layer_keywordless = ( not is_keyword_version_supported(kw_ver)) else: self.is_selected_layer_keywordless = True description = layer_description_html(layer, keywords) return description
def get_layer_description_from_canvas(self, layer, purpose): """Obtain the description of a canvas layer selected by user. :param layer: The QGIS layer. :type layer: QgsMapLayer :param category: The category of the layer to get the description. :type category: string :returns: description of the selected layer. :rtype: string """ if not layer: return "" try: keywords = self.keyword_io.read_keywords(layer) if 'layer_purpose' not in keywords: keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # set the current layer (e.g. for the keyword creation sub-thread) self.layer = layer if purpose == 'hazard': self.hazard_layer = layer elif purpose == 'exposure': self.exposure_layer = layer else: self.aggregation_layer = layer # Check if the layer is keywordless if keywords and 'keyword_version' in keywords: kw_ver = str(keywords['keyword_version']) self.is_selected_layer_keywordless = ( not is_keyword_version_supported(kw_ver)) else: self.is_selected_layer_keywordless = True desc = layer_description_html(layer, keywords) return desc
def layer_description_html(layer, keywords=None): """Form a html description of a given layer based on the layer parameters and keywords if provided :param layer: The layer to get the description :type layer: QgsMapLayer :param keywords: The layer keywords :type keywords: None, dict :returns: The html description in tabular format, ready to use in a label or tool tip. :rtype: str """ if keywords and 'keyword_version' in keywords: keyword_version = str(keywords['keyword_version']) else: keyword_version = None if (keywords and keyword_version and is_keyword_version_supported(keyword_version)): # The layer has valid keywords purpose = keywords.get('layer_purpose') if purpose == layer_purpose_hazard['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Hazard'), keywords.get(purpose)) unit = keywords.get('continuous_hazard_unit') elif purpose == layer_purpose_exposure['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Exposure'), keywords.get(purpose)) unit = keywords.get('exposure_unit') else: subcategory = '' unit = None if keywords.get('layer_mode') == layer_mode_classified['key']: unit = tr('classified data') if unit: unit = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % (tr('Unit'), unit) description = """ <table border="0" width="100%%"> <tr><td><b>%s</b>: </td><td>%s</td></tr> <tr><td><b>%s</b>: </td><td>%s</td></tr> %s %s <tr><td><b>%s</b>: </td><td>%s</td></tr> </table> """ % (tr('Title'), keywords.get('title'), tr('Purpose'), keywords.get('layer_purpose'), subcategory, unit, tr('Source'), keywords.get('source')) elif keywords: # The layer has keywords, but the version is wrong layer_version = keyword_version or tr('No Version') description = 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 update the keywords. Click Next if you want to ' 'assign keywords now.').format(layer_version=layer_version, inasafe_version=get_version()) else: # The layer is keywordless if is_point_layer(layer): geom_type = layer_geometry_point['key'] elif is_polygon_layer(layer): geom_type = layer_geometry_polygon['key'] else: geom_type = layer_geometry_line['key'] # hide password in the layer source source = layer.publicSource() description = """ %s<br/><br/> <b>%s</b>: %s<br/> <b>%s</b>: %s<br/><br/> %s """ % (tr('This layer has no valid keywords assigned'), tr('SOURCE'), source, tr('TYPE'), is_raster_layer(layer) and 'raster' or 'vector (%s)' % geom_type, tr('In the next step you will be able' + ' to assign keywords to this layer.')) return description
def get_layer_description_from_browser(self, category): """Obtain the description of the browser layer selected by user. :param category: The category of the layer to get the description. :type category: string :returns: Tuple of boolean and string. Boolean is true if layer is validated as compatible for current role (impact function and category) and false otherwise. String contains a description of the selected layer or an error message. :rtype: tuple """ if category == 'hazard': browser = self.tvBrowserHazard elif category == 'exposure': browser = self.tvBrowserExposure elif category == 'aggregation': browser = self.tvBrowserAggregation else: raise InaSAFEError index = browser.selectionModel().currentIndex() if not index: return False, '' # Map the proxy model index to the source model index index = browser.model().mapToSource(index) item = browser.model().sourceModel().dataItem(index) if not item: return False, '' item_class_name = item.metaObject().className() # if not itemClassName.endswith('LayerItem'): if not item.type() == QgsDataItem.Layer: if item_class_name == 'QgsPGRootItem' and not item.children(): return False, create_postGIS_connection_first else: return False, '' if item_class_name not in [ 'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsPGLayerItem', 'QgsLayerItem', ]: return False, '' path = item.path() if item_class_name in ['QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsLayerItem'] and not os.path.exists(path): return False, '' # try to create the layer if item_class_name == 'QgsOgrLayerItem': layer = QgsVectorLayer(path, '', 'ogr') elif item_class_name == 'QgsPGLayerItem': uri = self.pg_path_to_uri(path) if uri: layer = QgsVectorLayer(uri.uri(), uri.table(), 'postgres') else: layer = None else: layer = QgsRasterLayer(path, '', 'gdal') if not layer or not layer.isValid(): return False, self.tr('Not a valid layer.') try: keywords = self.keyword_io.read_keywords(layer) if ('layer_purpose' not in keywords and 'impact_summary' not in keywords): keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError, MissingMetadata): keywords = None # set the layer name for further use in the step_fc_summary if keywords: layer.setLayerName(keywords.get('title')) if not self.parent.is_layer_compatible(layer, category, keywords): label_text = '%s<br/>%s' % ( self.tr( 'This layer\'s keywords or type are not suitable:'), self.unsuitable_layer_description_html( layer, category, keywords)) return False, label_text # set the current layer (e.g. for the keyword creation sub-thread # or for adding the layer to mapCanvas) self.parent.layer = layer if category == 'hazard': self.parent.hazard_layer = layer elif category == 'exposure': self.parent.exposure_layer = layer else: self.parent.aggregation_layer = layer # Check if the layer is keywordless if keywords and 'keyword_version' in keywords: kw_ver = str(keywords['keyword_version']) self.parent.is_selected_layer_keywordless = ( not is_keyword_version_supported(kw_ver)) else: self.parent.is_selected_layer_keywordless = True desc = layer_description_html(layer, keywords) return True, desc
def is_layer_compatible(self, layer, layer_purpose=None, keywords=None): """Validate if a given layer is compatible for selected IF as a given layer_purpose :param layer: The layer to be validated :type layer: QgsVectorLayer | QgsRasterLayer :param layer_purpose: The layer_purpose the layer is validated for :type layer_purpose: None, string :param keywords: The layer keywords :type keywords: None, dict :returns: True if layer is appropriate for the selected role :rtype: boolean """ # If not explicitly stated, find the desired purpose # from the parent step if not layer_purpose: layer_purpose = self.get_parent_mode_constraints()[0]['key'] # If not explicitly stated, read the layer's keywords if not keywords: try: keywords = self.keyword_io.read_keywords(layer) if ('layer_purpose' not in keywords and 'impact_summary' not in keywords): keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # Get allowed subcategory and layer_geometry from IF constraints h, e, hc, ec = self.selected_impact_function_constraints() if layer_purpose == 'hazard': subcategory = h['key'] layer_geometry = hc['key'] elif layer_purpose == 'exposure': subcategory = e['key'] layer_geometry = ec['key'] else: # For aggregation layers, use a simplified test and return if (keywords and 'layer_purpose' in keywords and keywords['layer_purpose'] == layer_purpose): return True if not keywords and is_polygon_layer(layer): return True return False # Compare layer properties with explicitly set constraints # Reject if layer geometry doesn't match if layer_geometry != self.get_layer_geometry_id(layer): return False # If no keywords, there's nothing more we can check. # The same if the keywords version doesn't match if not keywords or 'keyword_version' not in keywords: return True keyword_version = str(keywords['keyword_version']) if not is_keyword_version_supported(keyword_version): return True # Compare layer keywords with explicitly set constraints # Reject if layer purpose missing or doesn't match if ('layer_purpose' not in keywords or keywords['layer_purpose'] != layer_purpose): return False # Reject if layer subcategory doesn't match if (layer_purpose in keywords and keywords[layer_purpose] != subcategory): return False # Compare layer keywords with the chosen function's constraints imfunc = self.step_fc_function.selected_function() lay_req = imfunc['layer_requirements'][layer_purpose] # Reject if layer mode doesn't match if ('layer_mode' in keywords and lay_req['layer_mode']['key'] != keywords['layer_mode']): return False # Reject if classification doesn't match classification_key = '%s_%s_classification' % ( 'raster' if is_raster_layer(layer) else 'vector', layer_purpose) classification_keys = classification_key + 's' if (lay_req['layer_mode'] == layer_mode_classified and classification_key in keywords and classification_keys in lay_req): allowed_classifications = [ c['key'] for c in lay_req[classification_keys]] if keywords[classification_key] not in allowed_classifications: return False # Reject if unit doesn't match unit_key = ('continuous_hazard_unit' if layer_purpose == layer_purpose_hazard['key'] else 'exposure_unit') unit_keys = unit_key + 's' if (lay_req['layer_mode'] == layer_mode_continuous and unit_key in keywords and unit_keys in lay_req): allowed_units = [ c['key'] for c in lay_req[unit_keys]] if keywords[unit_key] not in allowed_units: return False # Finally return True return True
def check_input_layer(layer, purpose): """Function to check if the layer is valid. The function will also set the monkey patching if needed. :param layer: The layer to test. :type layer: QgsMapLayer :param purpose: The expected purpose of the layer. :type purpose: basestring :return: A tuple with the status of the layer and an error message if needed. The status is 0 if everything was fine. The status is 1 if the client should fix something. :rtype: (int, m.Message) """ if not layer.isValid(): title = tr( 'The {purpose} layer is invalid').format(purpose=purpose) content = tr( 'The impact function needs a {exposure} layer to run. ' 'You must provide a valid {exposure} layer.').format( purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message # We should read it using KeywordIO for the very beginning. To avoid # get the modified keywords in the patching. try: keywords = KeywordIO().read_keywords(layer) except NoKeywordsFoundError: title = tr( 'The {purpose} layer does not have keywords.').format( purpose=purpose) content = tr( 'The {purpose} layer does not have keywords. Use the wizard ' 'to assign keywords to the layer.').format(purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message if keywords.get('layer_purpose') != purpose: title = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) content = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message version = keywords.get(inasafe_keyword_version_key) supported = is_keyword_version_supported(version) if not supported: parameters = { 'version': inasafe_keyword_version, 'source': layer.publicSource() } title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated to {version}.').format( **parameters) message = generate_input_error_message( title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message layer.keywords = keywords if is_vector_layer(layer): try: check_inasafe_fields(layer, keywords_only=True) except InvalidLayerError: title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated with the keyword ' 'wizard. Your fields which have been set in the keywords ' 'previously are not matching your layer.').format( source=layer.publicSource()) message = generate_input_error_message( title, m.Paragraph(content)) del layer.keywords return PREPARE_FAILED_BAD_INPUT, message return PREPARE_SUCCESS, None
def layer_description_html(layer, keywords=None): """Form a html description of a given layer based on the layer parameters and keywords if provided :param layer: The layer to get the description :type layer: QgsMapLayer :param keywords: The layer keywords :type keywords: None, dict :returns: The html description in tabular format, ready to use in a label or tool tip. :rtype: str """ if keywords and 'keyword_version' in keywords: keyword_version = str(keywords['keyword_version']) else: keyword_version = None if (keywords and keyword_version and is_keyword_version_supported(keyword_version)): # The layer has valid keywords purpose = keywords.get('layer_purpose') if purpose == layer_purpose_hazard['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Hazard'), keywords.get(purpose)) unit = keywords.get('continuous_hazard_unit') elif purpose == layer_purpose_exposure['key']: subcategory = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Exposure'), keywords.get(purpose)) unit = keywords.get('exposure_unit') else: subcategory = '' unit = None if keywords.get('layer_mode') == layer_mode_classified['key']: unit = tr('classified data') if unit: unit = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( tr('Unit'), unit) desc = """ <table border="0" width="100%%"> <tr><td><b>%s</b>: </td><td>%s</td></tr> <tr><td><b>%s</b>: </td><td>%s</td></tr> %s %s <tr><td><b>%s</b>: </td><td>%s</td></tr> </table> """ % (tr('Title'), keywords.get('title'), tr('Purpose'), keywords.get('layer_purpose'), subcategory, unit, tr('Source'), keywords.get('source')) elif keywords: # The layer has keywords, but the version is wrong layer_version = keyword_version or tr('No Version') desc = 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 update the keywords. Click Next if you want to ' 'assign keywords now.').format( layer_version=layer_version, inasafe_version=get_version()) else: # The layer is keywordless if is_point_layer(layer): geom_type = 'point' elif is_polygon_layer(layer): geom_type = 'polygon' else: geom_type = 'line' # hide password in the layer source source = layer.publicSource() desc = """ %s<br/><br/> <b>%s</b>: %s<br/> <b>%s</b>: %s<br/><br/> %s """ % (tr('This layer has no valid keywords assigned'), tr('SOURCE'), source, tr('TYPE'), is_raster_layer(layer) and 'raster' or 'vector (%s)' % geom_type, tr('In the next step you will be able' + ' to assign keywords to this layer.')) return desc
def __init__(self, parent=None, iface=None): """Constructor.""" QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'show-metadata-converter.svg') self.setWindowIcon(QIcon(icon)) self.setWindowTitle(self.tr('InaSAFE Metadata Converter')) self.parent = parent self.iface = iface self.keyword_io = KeywordIO() self.layer = None # Setup header label self.header_label.setText( tr('In this tool, you can convert a metadata 4.x of a layer to ' 'metadata 3.5. You will get a directory contains all the files of ' 'the layer and the new 3.5 metadata. If you want to convert ' 'hazard layer, you need to choose what exposure that you want to ' 'work with.')) # Setup input layer combo box # Filter our layers excepted_layers = [] for i in range(self.input_layer_combo_box.count()): layer = self.input_layer_combo_box.layer(i) try: keywords = self.keyword_io.read_keywords(layer) except (KeywordNotFoundError, NoKeywordsFoundError): # Filter out if no keywords excepted_layers.append(layer) continue layer_purpose = keywords.get('layer_purpose') if not layer_purpose: # Filter out if no layer purpose excepted_layers.append(layer) continue if layer_purpose not in accepted_layer_purposes: # Filter out if not aggregation, hazard, or exposure layer excepted_layers.append(layer) continue keyword_version = keywords.get('keyword_version') if not keyword_version: # Filter out if no keyword version excepted_layers.append(layer) continue if not is_keyword_version_supported(keyword_version): # Filter out if the version is not supported (4.x) excepted_layers.append(layer) continue self.input_layer_combo_box.setExceptedLayerList(excepted_layers) # Select the active layer. if self.iface.activeLayer(): found = self.input_layer_combo_box.findText( self.iface.activeLayer().name()) if found > -1: self.input_layer_combo_box.setLayer(self.iface.activeLayer()) # Set current layer as the active layer if self.input_layer_combo_box.currentLayer(): self.set_layer(self.input_layer_combo_box.currentLayer()) # Signals self.input_layer_combo_box.layerChanged.connect(self.set_layer) self.output_path_tool.clicked.connect(self.select_output_directory) # Set widget to show the main page (not help page) self.main_stacked_widget.setCurrentIndex(1) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) # Set up things for ok button self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) # The bug is fixed in QT 5.4 if QT_VERSION < 0x050400: self.resized.connect(self.after_resize)
def check_input_layer(layer, purpose): """Function to check if the layer is valid. The function will also set the monkey patching if needed. :param layer: The layer to test. :type layer: QgsMapLayer :param purpose: The expected purpose of the layer. :type purpose: basestring :return: A tuple with the status of the layer and an error message if needed. The status is 0 if everything was fine. The status is 1 if the client should fix something. :rtype: (int, m.Message) """ if not layer.isValid(): title = tr('The {purpose} layer is invalid').format(purpose=purpose) content = tr('The impact function needs a {exposure} layer to run. ' 'You must provide a valid {exposure} layer.').format( purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message # We should read it using KeywordIO for the very beginning. To avoid # get the modified keywords in the patching. try: keywords = KeywordIO().read_keywords(layer) except NoKeywordsFoundError: title = tr('The {purpose} layer does not have keywords.').format( purpose=purpose) content = tr( 'The {purpose} layer does not have keywords. Use the wizard ' 'to assign keywords to the layer.').format(purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message if keywords.get('layer_purpose') != purpose: title = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) content = tr('The expected {purpose} layer is not an {purpose}.') \ .format(purpose=purpose) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message version = keywords.get(inasafe_keyword_version_key) supported = is_keyword_version_supported(version) if not supported: parameters = { 'version': inasafe_keyword_version, 'source': layer.publicSource() } title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr('The layer {source} must be updated to {version}.' ).format(**parameters) message = generate_input_error_message(title, m.Paragraph(content)) return PREPARE_FAILED_BAD_INPUT, message layer.keywords = keywords if is_vector_layer(layer): try: check_inasafe_fields(layer, keywords_only=True) except InvalidLayerError: title = tr('The {purpose} layer is not up to date.').format( purpose=purpose) content = tr( 'The layer {source} must be updated with the keyword ' 'wizard. Your fields which have been set in the keywords ' 'previously are not matching your layer.').format( source=layer.publicSource()) message = generate_input_error_message(title, m.Paragraph(content)) del layer.keywords return PREPARE_FAILED_BAD_INPUT, message return PREPARE_SUCCESS, None
def is_layer_compatible(self, layer, layer_purpose=None, keywords=None): """Validate if a given layer is compatible for selected IF as a given layer_purpose :param layer: The layer to be validated :type layer: QgsVectorLayer | QgsRasterLayer :param layer_purpose: The layer_purpose the layer is validated for :type layer_purpose: None, string :param keywords: The layer keywords :type keywords: None, dict :returns: True if layer is appropriate for the selected role :rtype: boolean """ # If not explicitly stated, find the desired purpose # from the parent step if not layer_purpose: layer_purpose = self.get_parent_mode_constraints()[0]['key'] # If not explicitly stated, read the layer's keywords if not keywords: try: keywords = self.keyword_io.read_keywords(layer) if ('layer_purpose' not in keywords and 'impact_summary' not in keywords): keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # Get allowed subcategory and layer_geometry from IF constraints h, e, hc, ec = self.selected_impact_function_constraints() if layer_purpose == 'hazard': subcategory = h['key'] layer_geometry = hc['key'] elif layer_purpose == 'exposure': subcategory = e['key'] layer_geometry = ec['key'] else: # For aggregation layers, use a simplified test and return if (keywords and 'layer_purpose' in keywords and keywords['layer_purpose'] == layer_purpose): return True if not keywords and is_polygon_layer(layer): return True return False # Compare layer properties with explicitly set constraints # Reject if layer geometry doesn't match if layer_geometry != self.get_layer_geometry_key(layer): return False # If no keywords, there's nothing more we can check. # The same if the keywords version doesn't match if not keywords or 'keyword_version' not in keywords: return True keyword_version = str(keywords['keyword_version']) if not is_keyword_version_supported(keyword_version): return True # Compare layer keywords with explicitly set constraints # Reject if layer purpose missing or doesn't match if ('layer_purpose' not in keywords or keywords['layer_purpose'] != layer_purpose): return False # Reject if layer subcategory doesn't match if (layer_purpose in keywords and keywords[layer_purpose] != subcategory): return False return True
def is_layer_compatible(self, layer, layer_purpose=None, keywords=None): """Validate if a given layer is compatible for selected IF as a given layer_purpose :param layer: The layer to be validated :type layer: QgsVectorLayer | QgsRasterLayer :param layer_purpose: The layer_purpose the layer is validated for :type layer_purpose: None, string :param keywords: The layer keywords :type keywords: None, dict :returns: True if layer is appropriate for the selected role :rtype: boolean """ # If not explicitly stated, find the desired purpose # from the parent step if not layer_purpose: layer_purpose = self.get_parent_mode_constraints()[0]['key'] # If not explicitly stated, read the layer's keywords if not keywords: try: keywords = self.keyword_io.read_keywords(layer) if ('layer_purpose' not in keywords and 'impact_summary' not in keywords): keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # Get allowed subcategory and layer_geometry from IF constraints h, e, hc, ec = self.selected_impact_function_constraints() if layer_purpose == 'hazard': subcategory = h['key'] layer_geometry = hc['key'] elif layer_purpose == 'exposure': subcategory = e['key'] layer_geometry = ec['key'] else: # For aggregation layers, use a simplified test and return if (keywords and 'layer_purpose' in keywords and keywords['layer_purpose'] == layer_purpose): return True if not keywords and is_polygon_layer(layer): return True return False # Compare layer properties with explicitly set constraints # Reject if layer geometry doesn't match if layer_geometry != self.get_layer_geometry_id(layer): return False # If no keywords, there's nothing more we can check. # The same if the keywords version doesn't match if not keywords or 'keyword_version' not in keywords: return True keyword_version = str(keywords['keyword_version']) if not is_keyword_version_supported(keyword_version): return True # Compare layer keywords with explicitly set constraints # Reject if layer purpose missing or doesn't match if ('layer_purpose' not in keywords or keywords['layer_purpose'] != layer_purpose): return False # Reject if layer subcategory doesn't match if (layer_purpose in keywords and keywords[layer_purpose] != subcategory): return False # Compare layer keywords with the chosen function's constraints imfunc = self.step_fc_function.selected_function() lay_req = imfunc['layer_requirements'][layer_purpose] # Reject if layer mode doesn't match if ('layer_mode' in keywords and lay_req['layer_mode']['key'] != keywords['layer_mode']): return False # Reject if classification doesn't match classification_key = '%s_%s_classification' % ( 'raster' if is_raster_layer(layer) else 'vector', layer_purpose) classification_keys = classification_key + 's' if (lay_req['layer_mode'] == layer_mode_classified and classification_key in keywords and classification_keys in lay_req): allowed_classifications = [ c['key'] for c in lay_req[classification_keys] ] if keywords[classification_key] not in allowed_classifications: return False # Reject if unit doesn't match unit_key = ('continuous_hazard_unit' if layer_purpose == layer_purpose_hazard['key'] else 'exposure_unit') unit_keys = unit_key + 's' if (lay_req['layer_mode'] == layer_mode_continuous and unit_key in keywords and unit_keys in lay_req): allowed_units = [c['key'] for c in lay_req[unit_keys]] if keywords[unit_key] not in allowed_units: return False # Finally return True return True
def is_layer_compatible(self, layer, layer_purpose=None, keywords=None): """Validate if a given layer is compatible for selected IF as a given layer_purpose :param layer: The layer to be validated :type layer: QgsVectorLayer | QgsRasterLayer :param layer_purpose: The layer_purpose the layer is validated for :type layer_purpose: None, string :param keywords: The layer keywords :type keywords: None, dict :returns: True if layer is appropriate for the selected role :rtype: boolean """ # If not explicitly stated, find the desired purpose # from the parent step if not layer_purpose: layer_purpose = self.get_parent_mode_constraints()[0]['key'] # If not explicitly stated, read the layer's keywords if not keywords: try: keywords = self.keyword_io.read_keywords(layer) if 'layer_purpose' not in keywords: keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): keywords = None # Get allowed subcategory and layer_geometry from IF constraints h, e, hc, ec = self.selected_impact_function_constraints() if layer_purpose == 'hazard': subcategory = h['key'] layer_geometry = hc['key'] elif layer_purpose == 'exposure': subcategory = e['key'] layer_geometry = ec['key'] else: # For aggregation layers, use a simplified test and return if (keywords and 'layer_purpose' in keywords and keywords['layer_purpose'] == layer_purpose): return True if not keywords and is_polygon_layer(layer): return True return False # Compare layer properties with explicitly set constraints # Reject if layer geometry doesn't match if layer_geometry != self.get_layer_geometry_key(layer): return False # If no keywords, there's nothing more we can check. # The same if the keywords version doesn't match if not keywords or 'keyword_version' not in keywords: return True keyword_version = str(keywords['keyword_version']) if not is_keyword_version_supported(keyword_version): return True # Compare layer keywords with explicitly set constraints # Reject if layer purpose missing or doesn't match if ('layer_purpose' not in keywords or keywords['layer_purpose'] != layer_purpose): return False # Reject if layer subcategory doesn't match if (layer_purpose in keywords and keywords[layer_purpose] != subcategory): return False return True
def __init__(self, parent=None, iface=None): """Constructor.""" QDialog.__init__(self, parent) self.setupUi(self) icon = resources_path('img', 'icons', 'show-metadata-converter.svg') self.setWindowIcon(QIcon(icon)) self.setWindowTitle(self.tr('InaSAFE Metadata Converter')) self.parent = parent self.iface = iface self.keyword_io = KeywordIO() self.layer = None # Setup header label self.header_label.setText(tr( 'In this tool, you can convert a metadata 4.x of a layer to ' 'metadata 3.5. You will get a directory contains all the files of ' 'the layer and the new 3.5 metadata. If you want to convert ' 'hazard layer, you need to choose what exposure that you want to ' 'work with.') ) # Setup input layer combo box # Filter our layers excepted_layers = [] for i in range(self.input_layer_combo_box.count()): layer = self.input_layer_combo_box.layer(i) try: keywords = self.keyword_io.read_keywords(layer) except (KeywordNotFoundError, NoKeywordsFoundError): # Filter out if no keywords excepted_layers.append(layer) continue layer_purpose = keywords.get('layer_purpose') if not layer_purpose: # Filter out if no layer purpose excepted_layers.append(layer) continue if layer_purpose not in accepted_layer_purposes: # Filter out if not aggregation, hazard, or exposure layer excepted_layers.append(layer) continue keyword_version = keywords.get('keyword_version') if not keyword_version: # Filter out if no keyword version excepted_layers.append(layer) continue if not is_keyword_version_supported(keyword_version): # Filter out if the version is not supported (4.x) excepted_layers.append(layer) continue self.input_layer_combo_box.setExceptedLayerList(excepted_layers) # Select the active layer. if self.iface.activeLayer(): found = self.input_layer_combo_box.findText( self.iface.activeLayer().name()) if found > -1: self.input_layer_combo_box.setLayer(self.iface.activeLayer()) # Set current layer as the active layer if self.input_layer_combo_box.currentLayer(): self.set_layer(self.input_layer_combo_box.currentLayer()) # Signals self.input_layer_combo_box.layerChanged.connect(self.set_layer) self.output_path_tool.clicked.connect(self.select_output_directory) # Set widget to show the main page (not help page) self.main_stacked_widget.setCurrentIndex(1) # Set up things for context help self.help_button = self.button_box.button(QDialogButtonBox.Help) # Allow toggling the help button self.help_button.setCheckable(True) self.help_button.toggled.connect(self.help_toggled) # Set up things for ok button self.ok_button = self.button_box.button(QDialogButtonBox.Ok) self.ok_button.clicked.connect(self.accept) # Set up things for cancel button self.cancel_button = self.button_box.button(QDialogButtonBox.Cancel) self.cancel_button.clicked.connect(self.reject) # The bug is fixed in QT 5.4 if QT_VERSION < 0x050400: self.resized.connect(self.after_resize)
def get_layer_description_from_browser(self, category): """Obtain the description of the browser layer selected by user. :param category: The category of the layer to get the description. :type category: string :returns: Tuple of boolean and string. Boolean is true if layer is validated as compatible for current role (impact function and category) and false otherwise. String contains a description of the selected layer or an error message. :rtype: tuple """ if category == 'hazard': browser = self.tvBrowserHazard elif category == 'exposure': browser = self.tvBrowserExposure elif category == 'aggregation': browser = self.tvBrowserAggregation else: raise InaSAFEError index = browser.selectionModel().currentIndex() if not index: return False, '' # Map the proxy model index to the source model index index = browser.model().mapToSource(index) item = browser.model().sourceModel().dataItem(index) if not item: return False, '' item_class_name = item.metaObject().className() # if not itemClassName.endswith('LayerItem'): if not item.type() == QgsDataItem.Layer: if item_class_name == 'QgsPGRootItem' and not item.children(): return False, create_postGIS_connection_first else: return False, '' if item_class_name not in [ 'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsPGLayerItem', 'QgsLayerItem', ]: return False, '' path = item.path() if item_class_name in [ 'QgsOgrLayerItem', 'QgsGdalLayerItem', 'QgsLayerItem' ] and not os.path.exists(path): return False, '' # try to create the layer if item_class_name == 'QgsOgrLayerItem': layer = QgsVectorLayer(path, '', 'ogr') elif item_class_name == 'QgsPGLayerItem': uri = self.postgis_path_to_uri(path) if uri: layer = QgsVectorLayer(uri.uri(), uri.table(), 'postgres') else: layer = None else: layer = QgsRasterLayer(path, '', 'gdal') if not layer or not layer.isValid(): return False, self.tr('Not a valid layer.') try: keywords = self.keyword_io.read_keywords(layer) if 'layer_purpose' not in keywords: keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError, MissingMetadata): keywords = None # set the layer name for further use in the step_fc_summary if keywords: if qgis_version() >= 21800: layer.setName(keywords.get('title')) else: layer.setLayerName(keywords.get('title')) if not self.parent.is_layer_compatible(layer, category, keywords): label_text = '%s<br/>%s' % ( self.tr('This layer\'s keywords or type are not suitable:'), self.unsuitable_layer_description_html(layer, category, keywords)) return False, label_text # set the current layer (e.g. for the keyword creation sub-thread # or for adding the layer to mapCanvas) self.parent.layer = layer if category == 'hazard': self.parent.hazard_layer = layer elif category == 'exposure': self.parent.exposure_layer = layer else: self.parent.aggregation_layer = layer # Check if the layer is keywordless if keywords and 'keyword_version' in keywords: kw_ver = str(keywords['keyword_version']) self.parent.is_selected_layer_keywordless = ( not is_keyword_version_supported(kw_ver)) else: self.parent.is_selected_layer_keywordless = True desc = layer_description_html(layer, keywords) return True, desc
def _create_exposure_combos(self): """Create one combobox for each exposure and insert them in the UI.""" # Map registry may be invalid if QGIS is shutting down project = QgsProject.instance() canvas_layers = self.iface.mapCanvas().layers() # MapLayers returns a QMap<QString id, QgsMapLayer layer> layers = list(project.mapLayers().values()) # Sort by name for tests layers.sort(key=lambda x: x.name()) show_only_visible_layers = setting('visibleLayersOnlyFlag', expected_type=bool) # For issue #618 if len(layers) == 0: # self.message_viewer.setHtml(getting_started_message()) return for one_exposure in exposure_all: label = QLabel(one_exposure['name']) combo = QComboBox() combo.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) combo.addItem(tr('Do not use'), None) self.form_layout.addRow(label, combo) self.combos_exposures[one_exposure['key']] = combo for layer in layers: if (show_only_visible_layers and (layer not in canvas_layers)): continue try: layer_purpose = self.keyword_io.read_keywords( layer, 'layer_purpose') keyword_version = str( self.keyword_io.read_keywords(layer, inasafe_keyword_version_key)) if not is_keyword_version_supported(keyword_version): continue except BaseException: # pylint: disable=W0702 # continue ignoring this layer continue # See if there is a title for this layer, if not, # fallback to the layer's filename # noinspection PyBroadException try: title = self.keyword_io.read_keywords(layer, 'title') except (NoKeywordsFoundError, KeywordNotFoundError, MetadataReadError): # Skip if there are no keywords at all, or missing keyword continue except BaseException: # pylint: disable=W0702 pass else: # Lookup internationalised title if available title = self.tr(title) # Register title with layer set_layer_from_title = setting('set_layer_from_title_flag', True, bool) if title and set_layer_from_title: if qgis_version() >= 21800: layer.setName(title) else: # QGIS 2.14 layer.setLayerName(title) source = layer.id() icon = layer_icon(layer) if layer_purpose == layer_purpose_hazard['key']: add_ordered_combo_item(self.cbx_hazard, title, source, icon=icon) elif layer_purpose == layer_purpose_aggregation['key']: if self.use_selected_only: count_selected = layer.selectedFeatureCount() if count_selected > 0: add_ordered_combo_item(self.cbx_aggregation, title, source, count_selected, icon=icon) else: add_ordered_combo_item(self.cbx_aggregation, title, source, None, icon) else: add_ordered_combo_item(self.cbx_aggregation, title, source, None, icon) elif layer_purpose == layer_purpose_exposure['key']: # fetching the exposure try: exposure_type = self.keyword_io.read_keywords( layer, layer_purpose_exposure['key']) except BaseException: # pylint: disable=W0702 # continue ignoring this layer continue for key, combo in list(self.combos_exposures.items()): if key == exposure_type: add_ordered_combo_item(combo, title, source, icon=icon) self.cbx_aggregation.addItem(entire_area_item_aggregation, None) for combo in list(self.combos_exposures.values()): combo.currentIndexChanged.connect(self.validate_impact_function)