Source code for safe.gui.tools.wizard_dialog

# coding=utf-8
"""
InaSAFE Disaster risk assessment tool by AusAid **GUI InaSAFE Wizard Dialog.**

Contact : [email protected]

.. note:: This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.

.. todo:: Check raster is single band

"""
__author__ = '[email protected]'
__revision__ = '$Format:%H$'
__date__ = '21/02/2011'
__copyright__ = ('Copyright 2012, Australia Indonesia Facility for '
                 'Disaster Reduction')

import os
import logging
import re
import json
from collections import OrderedDict
from sqlite3 import OperationalError

from osgeo import gdal
from osgeo.gdalconst import GA_ReadOnly
import numpy

from qgis.core import (
    QgsCoordinateTransform,
    QgsBrowserModel,
    QgsDataItem,
    QgsVectorLayer,
    QgsRasterLayer,
    QgsDataSourceURI,
    QgsMapLayerRegistry)

# noinspection PyPackageRequirements
from PyQt4 import QtGui, QtCore
# noinspection PyPackageRequirements
from PyQt4.QtCore import pyqtSignature, QSettings, QPyNullVariant
# noinspection PyPackageRequirements
from PyQt4.QtGui import (
    QDialog,
    QListWidgetItem,
    QPixmap,
    QSortFilterProxyModel)

# pylint: disable=F0401
from db_manager.db_plugins.postgis.connector import PostGisDBConnector
# pylint: enable=F0401

# pylint: disable=unused-import
# TODO: to get rid of the following import,
# TODO: we need to get rid of all those evals...TS
from safe import definitions
# pylint: enable=unused-import
from safe.definitions import (
    global_default_attribute,
    do_not_use_attribute,
    continuous_hazard_unit,
    exposure_unit,
    raster_hazard_classification,
    vector_hazard_classification,
    layer_purpose_hazard,
    layer_purpose_exposure,
    layer_purpose_aggregation,
    hazard_category_single_event,
    layer_geometry_point,
    layer_geometry_line,
    layer_geometry_polygon,
    layer_geometry_raster,
    layer_mode_continuous,
    layer_mode_classified)
from safe.impact_functions.impact_function_manager import ImpactFunctionManager
from safe.utilities.keyword_io import KeywordIO
from safe.utilities.analysis_handler import AnalysisHandler
from safe.utilities.gis import (
    is_raster_layer,
    is_point_layer,
    is_polygon_layer,
    layer_attribute_names)
from safe.utilities.utilities import get_error_message, compare_version
from safe.defaults import get_defaults
from safe.common.exceptions import (
    HashNotFoundError,
    NoKeywordsFoundError,
    KeywordNotFoundError,
    InvalidParameterError,
    UnsupportedProviderError,
    InsufficientOverlapError,
    InaSAFEError)
from safe.common.resource_parameter import ResourceParameter
from safe.common.version import get_version
from safe_extras.parameters.group_parameter import GroupParameter
from safe.utilities.resources import get_ui_class, resources_path
from safe.gui.tools.function_options_dialog import (
    FunctionOptionsDialog)
from safe.utilities.unicode import get_unicode
from safe.utilities.i18n import tr
from safe.gui.tools.wizard_strings import (
    category_question,
    category_question_hazard,
    category_question_exposure,
    category_question_aggregation,
    hazard_category_question,
    layermode_raster_question,
    layermode_vector_question,
    unit_question,
    allow_resampling_question,
    field_question_subcategory_unit,
    field_question_subcategory_classified,
    field_question_aggregation,
    classification_question,
    classify_vector_question,
    classify_raster_question,
    select_function_constraints2_question,
    select_function_question,
    select_hazard_origin_question,
    select_hazlayer_from_canvas_question,
    select_hazlayer_from_browser_question,
    select_exposure_origin_question,
    select_explayer_from_canvas_question,
    select_explayer_from_browser_question,
    create_postGIS_connection_first)

# TODO(Ismail): We need a better way to import all of these string
# pylint: disable=unused-import
from safe.gui.tools.wizard_strings import (
    earthquake_mmi_question,
    exposure_question,
    flood_feet_depth_question,
    flood_metres_depth_question,
    flood_wetdry_question,
    hazard_question,
    population_density_question,
    population_number_question,
    road_road_type_question,
    structure_building_type_question,
    tephra_kgm2_question,
    tsunami_feet_depth_question,
    tsunami_metres_depth_question,
    tsunami_wetdry_question,
    volcano_volcano_categorical_question
)
# pylint: enable=unused-import

LOGGER = logging.getLogger('InaSAFE')

FORM_CLASS = get_ui_class('wizard_dialog_base.ui')


# Constants: tab numbers for steps
step_kw_category = 1
step_kw_subcategory = 2
step_kw_hazard_category = 3
step_kw_layermode = 4
step_kw_unit = 5
step_kw_classification = 6
step_kw_field = 7
step_kw_resample = 8
step_kw_classify = 9
step_kw_extrakeywords = 10
step_kw_aggregation = 11
step_kw_source = 12
step_kw_title = 13
step_fc_function_1 = 14
step_fc_function_2 = 15
step_fc_function_3 = 16
step_fc_hazlayer_origin = 17
step_fc_hazlayer_from_canvas = 18
step_fc_hazlayer_from_browser = 19
step_fc_explayer_origin = 20
step_fc_explayer_from_canvas = 21
step_fc_explayer_from_browser = 22
step_fc_disjoint_layers = 23
step_fc_agglayer_origin = 24
step_fc_agglayer_from_canvas = 25
step_fc_agglayer_from_browser = 26
step_fc_agglayer_disjoint = 27
step_fc_extent = 28
step_fc_extent_disjoint = 29
step_fc_params = 30
step_fc_summary = 31
step_fc_analysis = 32


# Aggregations' keywords
DEFAULTS = get_defaults()
female_ratio_attribute_key = DEFAULTS['FEMALE_RATIO_ATTR_KEY']
female_ratio_default_key = DEFAULTS['FEMALE_RATIO_KEY']
youth_ratio_attribute_key = DEFAULTS['YOUTH_RATIO_ATTR_KEY']
youth_ratio_default_key = DEFAULTS['YOUTH_RATIO_KEY']
adult_ratio_attribute_key = DEFAULTS['ADULT_RATIO_ATTR_KEY']
adult_ratio_default_key = DEFAULTS['ADULT_RATIO_KEY']
elderly_ratio_attribute_key = DEFAULTS['ELDERLY_RATIO_ATTR_KEY']
elderly_ratio_default_key = DEFAULTS['ELDERLY_RATIO_KEY']

# Data roles
RoleFunctions = QtCore.Qt.UserRole
RoleHazard = QtCore.Qt.UserRole + 1
RoleExposure = QtCore.Qt.UserRole + 2
RoleHazardConstraint = QtCore.Qt.UserRole + 3
RoleExposureConstraint = QtCore.Qt.UserRole + 4


[docs]def get_question_text(constant): """Find a constant by name and return its value. :param constant: The name of the constant to look for. :type constant: string :returns: The value of the constant or red error message. :rtype: string """ try: # TODO Eval = bad return eval(constant) # pylint: disable=eval-used except NameError: return '<b>MISSING CONSTANT: %s</b>' % constant
[docs]class LayerBrowserProxyModel(QSortFilterProxyModel): """Proxy model for hiding unsupported branches in the layer browser.""" def __init__(self, parent): """Constructor for the model. :param parent: Parent widget of this model. :type parent: QWidget """ QSortFilterProxyModel.__init__(self, parent)
[docs] def filterAcceptsRow(self, source_row, source_parent): """The filter method .. note:: This filter hides top-level items of unsupported branches and also leaf items containing xml files. Enabled root items: QgsDirectoryItem, QgsFavouritesItem, QgsPGRootItem. Disabled root items: QgsMssqlRootItem, QgsSLRootItem, QgsOWSRootItem, QgsWCSRootItem, QgsWFSRootItem, QgsWMSRootItem. Disabled leaf items: QgsLayerItem and QgsOgrLayerItem with path ending with '.xml' :param source_row: Parent widget of the model :type source_row: int :param source_parent: Parent item index :type source_parent: QModelIndex :returns: Item validation result :rtype: bool """ source_index = self.sourceModel().index(source_row, 0, source_parent) item = self.sourceModel().dataItem(source_index) if item.metaObject().className() in [ 'QgsMssqlRootItem', 'QgsSLRootItem', 'QgsOWSRootItem', 'QgsWCSRootItem', 'QgsWFSRootItem', 'QgsWMSRootItem']: return False if (item.metaObject().className() in [ 'QgsLayerItem', 'QgsOgrLayerItem'] and item.path().endswith('.xml')): return False return True
[docs]class WizardDialog(QDialog, FORM_CLASS): """Dialog implementation class for the InaSAFE wizard.""" def __init__(self, parent=None, iface=None, dock=None): """Constructor for the dialog. .. note:: In QtDesigner the advanced editor's predefined keywords list should be shown in english always, so when adding entries to cboKeyword, be sure to choose :safe_qgis:`Properties<<` and untick the :safe_qgis:`translatable` property. :param parent: Parent widget of this dialog. :type parent: QWidget :param iface: QGIS QGisAppInterface instance. :type iface: QGisAppInterface :param dock: Dock widget instance that we can notify of changes to the keywords. Optional. :type dock: Dock """ QDialog.__init__(self, parent) self.setupUi(self) self.setWindowTitle('InaSAFE') # Constants self.keyword_creation_wizard_name = 'InaSAFE Keywords Creation Wizard' self.ifcw_name = 'InaSAFE Impact Function Centric Wizard' # Note the keys should remain untranslated as we need to write # english to the keywords file. # Save reference to the QGIS interface and parent self.iface = iface self.parent = parent self.dock = dock self.suppress_warning_dialog = False self.set_tool_tip() # Set icons self.lblMainIcon.setPixmap( QPixmap(resources_path('img', 'icons', 'icon-white.svg'))) self.lblIconDisjoint_1.setPixmap( QPixmap(resources_path('img', 'wizard', 'icon-stop.svg'))) self.lblIconDisjoint_2.setPixmap( QPixmap(resources_path('img', 'wizard', 'icon-stop.svg'))) self.lblIconDisjoint_3.setPixmap( QPixmap(resources_path('img', 'wizard', 'icon-stop.svg'))) # Set models for browsers browser_model = QgsBrowserModel() proxy_model = LayerBrowserProxyModel(self) proxy_model.setSourceModel(browser_model) self.tvBrowserHazard.setModel(proxy_model) browser_model = QgsBrowserModel() proxy_model = LayerBrowserProxyModel(self) proxy_model.setSourceModel(browser_model) self.tvBrowserExposure.setModel(proxy_model) browser_model = QgsBrowserModel() proxy_model = LayerBrowserProxyModel(self) proxy_model.setSourceModel(browser_model) self.tvBrowserAggregation.setModel(proxy_model) self.parameter_dialog = None self.extent_dialog = None self.keyword_io = KeywordIO() self.twParams = None self.swExtent = None self.is_selected_layer_keywordless = False self.parent_step = None self.pbnBack.setEnabled(False) self.pbnNext.setEnabled(False) # Collect some serial widgets self.extra_keywords_widgets = [ {'cbo': self.cboExtraKeyword1, 'lbl': self.lblExtraKeyword1}, {'cbo': self.cboExtraKeyword2, 'lbl': self.lblExtraKeyword2}, {'cbo': self.cboExtraKeyword3, 'lbl': self.lblExtraKeyword3}, {'cbo': self.cboExtraKeyword4, 'lbl': self.lblExtraKeyword4}, {'cbo': self.cboExtraKeyword5, 'lbl': self.lblExtraKeyword5}, {'cbo': self.cboExtraKeyword6, 'lbl': self.lblExtraKeyword6}, {'cbo': self.cboExtraKeyword7, 'lbl': self.lblExtraKeyword7}, {'cbo': self.cboExtraKeyword8, 'lbl': self.lblExtraKeyword8} ] for ekw in self.extra_keywords_widgets: ekw['key'] = None ekw['slave_key'] = None # noinspection PyUnresolvedReferences self.tvBrowserHazard.selectionModel().selectionChanged.connect( self.tvBrowserHazard_selection_changed) self.tvBrowserExposure.selectionModel().selectionChanged.connect( self.tvBrowserExposure_selection_changed) self.tvBrowserAggregation.selectionModel().selectionChanged.connect( self.tvBrowserAggregation_selection_changed) self.treeClasses.itemChanged.connect(self.update_dragged_item_flags) self.pbnCancel.released.connect(self.reject) # string constants self.global_default_string = global_default_attribute['name'] self.global_default_data = global_default_attribute['id'] self.do_not_use_string = do_not_use_attribute['name'] self.do_not_use_data = do_not_use_attribute['id'] self.defaults = get_defaults() # Initialize attributes self.impact_function_manager = ImpactFunctionManager() self.existing_keywords = None self.layer = None self.hazard_layer = None self.exposure_layer = None self.aggregation_layer = None self.if_params = None self.analysis_handler = None
[docs] def set_mode_label_to_keywords_creation(self): """Set the mode label to the Keywords Creation/Update mode """ self.setWindowTitle(self.keyword_creation_wizard_name) if self.get_existing_keyword('layer_purpose'): mode_name = (self.tr( 'Keywords update wizard for layer <b>%s</b>' ) % self.layer.name()) else: mode_name = (self.tr( 'Keywords creation wizard for layer <b>%s</b>' ) % self.layer.name()) self.lblSubtitle.setText(mode_name)
[docs] def set_mode_label_to_ifcw(self): """Set the mode label to the IFCW """ self.setWindowTitle(self.ifcw_name) self.lblSubtitle.setText(self.tr( 'Use this wizard to run a guided impact assessment'))
[docs] def set_keywords_creation_mode(self, layer=None): """Set the Wizard to the Keywords Creation mode :param layer: Layer to set the keywords for :type layer: QgsMapLayer """ self.layer = layer or self.iface.mapCanvas().currentLayer() try: self.existing_keywords = self.keyword_io.read_keywords(self.layer) # if 'layer_purpose' not in self.existing_keywords: # self.existing_keywords = None except (HashNotFoundError, OperationalError, NoKeywordsFoundError, KeywordNotFoundError, InvalidParameterError, UnsupportedProviderError): self.existing_keywords = None self.set_mode_label_to_keywords_creation() self.set_widgets_step_kw_category() self.go_to_step(step_kw_category)
[docs] def set_function_centric_mode(self): """Set the Wizard to the Function Centric mode""" self.set_mode_label_to_ifcw() new_step = step_fc_function_1 self.set_widgets_step_fc_function_1() self.pbnNext.setEnabled(self.is_ready_to_next_step(new_step)) self.go_to_step(new_step)
[docs] def update_MessageViewer_size(self): """Update maximumHeight size of the MessageViewer to fit its parent tab This is a workaround for a bug that makes MessageViewer flooding up to maximumHeight on Windows. """ self.wvResults.setMaximumHeight(self.pgF25Progress.height() - 90) # pylint: disable=unused-argument
[docs] def resizeEvent(self, ev): """Trigger MessageViewer size update on window resize .. note:: This is an automatic Qt slot executed when the window size changes. """ pass # self.update_MessageViewer_size() # pylint: disable=unused-argument
[docs] def purposes_for_layer(self): """Return a list of valid purposes for the current layer. :returns: A list where each value represents a valid purpose. :rtype: list """ layer_geometry_id = self.get_layer_geometry_id() return self.impact_function_manager.purposes_for_layer( layer_geometry_id)
[docs] def subcategories_for_layer(self): """Return a list of valid subcategories for a layer. Subcategory is hazard type or exposure type. :returns: A list where each value represents a valid subcategory. :rtype: list """ purpose = self.selected_category() layer_geometry_id = self.get_layer_geometry_id() if purpose == layer_purpose_hazard: return self.impact_function_manager.hazards_for_layer( layer_geometry_id) elif purpose == layer_purpose_exposure: return self.impact_function_manager.exposures_for_layer( layer_geometry_id)
[docs] def hazard_categories_for_layer(self): """Return a list of valid hazard categories for a layer. :returns: A list where each value represents a valid hazard category. :rtype: list """ layer_geometry_id = self.get_layer_geometry_id() if self.selected_category() != layer_purpose_hazard: return [] hazard_type_id = self.selected_subcategory()['key'] return self.impact_function_manager.hazard_categories_for_layer( layer_geometry_id, hazard_type_id)
[docs] def layermodes_for_layer(self): """Return a list of valid layer modes for a layer. :returns: A list where each value represents a valid layer mode. :rtype: list """ purpose = self.selected_category() subcategory = self.selected_subcategory() layer_geometry_id = self.get_layer_geometry_id() if purpose == layer_purpose_hazard: hazard_category = self.selected_hazard_category() return self.impact_function_manager.available_hazard_layer_modes( subcategory['key'], layer_geometry_id, hazard_category['key']) elif purpose == layer_purpose_exposure: return self.impact_function_manager.available_exposure_layer_modes( subcategory['key'], layer_geometry_id)
[docs] def classifications_for_layer(self): """Return a list of valid classifications for a layer. :returns: A list where each value represents a valid classification. :rtype: list """ layer_geometry_id = self.get_layer_geometry_id() layer_mode_id = self.selected_layermode()['key'] subcategory_id = self.selected_subcategory()['key'] if self.selected_category() == layer_purpose_hazard: hazard_category_id = self.selected_hazard_category()['key'] if is_raster_layer(self.layer): return self.impact_function_manager.\ raster_hazards_classifications_for_layer( subcategory_id, layer_geometry_id, layer_mode_id, hazard_category_id) else: return self.impact_function_manager\ .vector_hazards_classifications_for_layer( subcategory_id, layer_geometry_id, layer_mode_id, hazard_category_id) else: # There are no classifications for exposures defined yet return []
[docs] def additional_keywords_for_the_layer(self): """Return a list of valid additional keywords for the current layer. :returns: A list where each value represents a valid additional kw. :rtype: list """ layer_geometry_key = self.get_layer_geometry_id() layer_mode_key = self.selected_layermode()['key'] if self.selected_category() == layer_purpose_hazard: hazard_category_key = self.selected_hazard_category()['key'] hazard_key = self.selected_subcategory()['key'] return self.impact_function_manager.hazard_additional_keywords( layer_mode_key, layer_geometry_key, hazard_category_key, hazard_key) else: exposure_key = self.selected_subcategory()['key'] return self.impact_function_manager.exposure_additional_keywords( layer_mode_key, layer_geometry_key, exposure_key)
[docs] def field_keyword_for_the_layer(self): """Return the proper keyword for field for the current layer. Expected values are: 'field', 'structure_class_field', road_class_field :returns: the field keyword :rtype: string """ if self.selected_category() == layer_purpose_aggregation: # purpose: aggregation return 'aggregation attribute' elif self.selected_category() == layer_purpose_hazard: # purpose: hazard if (self.selected_layermode() == layer_mode_classified and is_point_layer(self.layer)): # No field for classified point hazards return '' else: # purpose: exposure layer_mode_key = self.selected_layermode()['key'] layer_geometry_key = self.get_layer_geometry_id() exposure_key = self.selected_subcategory()['key'] exposure_class_fields = self.impact_function_manager.\ exposure_class_fields( layer_mode_key, layer_geometry_key, exposure_key) if exposure_class_fields and len(exposure_class_fields) == 1: return exposure_class_fields[0]['key'] # Fallback to default return 'field' # =========================== # STEP_KW_CATEGORY # =========================== # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_lstCategories_itemSelectionChanged(self): """Update purpose description label. .. note:: This is an automatic Qt slot executed when the category selection changes. """ # Clear all further steps in order to properly calculate the prev step self.lstHazardCategories.clear() self.lstSubcategories.clear() self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets category = self.selected_category() # Exit if no selection if not category: return # Set description label self.lblDescribeCategory.setText(category["description"]) self.lblIconCategory.setPixmap(QPixmap( resources_path('img', 'wizard', 'keyword-category-%s.svg' % (category['key'] or 'notset')))) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_category(self): """Obtain the layer purpose selected by user. :returns: Metadata of the selected layer purpose. :rtype: dict, None """ item = self.lstCategories.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_category(self): """Set widgets on the layer purpose tab.""" # Clear all further steps in order to properly calculate the prev step self.lstHazardCategories.clear() self.lstSubcategories.clear() self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets self.lstCategories.clear() self.lblDescribeCategory.setText('') self.lblIconCategory.setPixmap(QPixmap()) self.lblSelectCategory.setText( category_question % self.layer.name()) categories = self.purposes_for_layer() if self.get_layer_geometry_id() == 'polygon': categories += ['aggregation'] for category in categories: if not isinstance(category, dict): # pylint: disable=eval-used category = eval('definitions.layer_purpose_%s' % category) # pylint: enable=eval-used item = QListWidgetItem(category['name'], self.lstCategories) item.setData(QtCore.Qt.UserRole, unicode(category)) self.lstCategories.addItem(item) # Check if layer keywords are already assigned category_keyword = self.get_existing_keyword('layer_purpose') # Check if it's KW mode embedded in IFCW mode if self.parent_step: if self.parent_step in [step_fc_hazlayer_from_canvas, step_fc_hazlayer_from_browser]: category_keyword = layer_purpose_hazard['key'] elif self.parent_step in [step_fc_explayer_from_canvas, step_fc_explayer_from_browser]: category_keyword = layer_purpose_exposure['key'] else: category_keyword = layer_purpose_aggregation['key'] # Set values based on existing keywords or parent mode if category_keyword: categories = [] for index in xrange(self.lstCategories.count()): item = self.lstCategories.item(index) # pylint: disable=eval-used category = eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used categories.append(category['key']) if category_keyword in categories: self.lstCategories.setCurrentRow( categories.index(category_keyword)) self.auto_select_one_item(self.lstCategories) # =========================== # STEP_KW_SUBCATEGORY # =========================== # noinspection PyPep8Naming
[docs] def on_lstSubcategories_itemSelectionChanged(self): """Update subcategory description label. .. note:: This is an automatic Qt slot executed when the subcategory selection changes. """ # Clear all further steps in order to properly calculate the prev step self.lstHazardCategories.clear() self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets subcategory = self.selected_subcategory() # Exit if no selection if not subcategory: return # Set description label self.lblDescribeSubcategory.setText(subcategory['description']) icon_path = resources_path('img', 'wizard', 'keyword-subcategory-%s.svg' % (subcategory['key'] or 'notset')) if not os.path.exists(icon_path): category = self.selected_category() icon_path = resources_path('img', 'wizard', 'keyword-category-%s.svg' % (category['key'])) self.lblIconSubcategory.setPixmap(QPixmap(icon_path)) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_subcategory(self): """Obtain the subcategory selected by user. :returns: Metadata of the selected subcategory. :rtype: dict, None """ item = self.lstSubcategories.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_subcategory(self): """Set widgets on the Subcategory tab.""" # Clear all further steps in order to properly calculate the prev step self.lstHazardCategories.clear() self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets category = self.selected_category() self.lstSubcategories.clear() self.lblDescribeSubcategory.setText('') self.lblIconSubcategory.setPixmap(QPixmap()) self.lblSelectSubcategory.setText( get_question_text('%s_question' % category['key'])) for i in self.subcategories_for_layer(): item = QListWidgetItem(i['name'], self.lstSubcategories) item.setData(QtCore.Qt.UserRole, unicode(i)) self.lstSubcategories.addItem(item) # Check if layer keywords are already assigned key = self.selected_category()['key'] keyword = self.get_existing_keyword(key) # Check if it's KW mode embedded in IFCW if self.parent_step: h, e, _hc, _ec = self.selected_impact_function_constraints() if self.parent_step in [step_fc_hazlayer_from_canvas, step_fc_hazlayer_from_browser]: keyword = h['key'] elif self.parent_step in [step_fc_explayer_from_canvas, step_fc_explayer_from_browser]: keyword = e['key'] # Set values based on existing keywords or parent mode if keyword: subcategories = [] for index in xrange(self.lstSubcategories.count()): item = self.lstSubcategories.item(index) # pylint: disable=eval-used subcategory = eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used subcategories.append(subcategory['key']) if keyword in subcategories: self.lstSubcategories.setCurrentRow( subcategories.index(keyword)) self.auto_select_one_item(self.lstSubcategories) # =========================== # STEP_KW_HAZARD_CATEGORY # =========================== # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_lstHazardCategories_itemSelectionChanged(self): """Update hazard category description label. .. note:: This is an automatic Qt slot executed when the category selection changes. """ # Clear all further steps in order to properly calculate the prev step self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets hazard_category = self.selected_hazard_category() # Exit if no selection if not hazard_category: return # Set description label self.lblDescribeHazardCategory.setText(hazard_category["description"]) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_hazard_category(self): """Obtain the hazard category selected by user. :returns: Metadata of the selected hazard category. :rtype: dict, None """ item = self.lstHazardCategories.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_hazard_category(self): """Set widgets on the Hazard Category tab.""" # Clear all further steps in order to properly calculate the prev step self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets self.lstHazardCategories.clear() self.lblDescribeHazardCategory.setText('') self.lblSelectHazardCategory.setText( hazard_category_question) hazard_categories = self.hazard_categories_for_layer() for hazard_category in hazard_categories: if not isinstance(hazard_category, dict): # pylint: disable=eval-used hazard_category = eval('definitions.hazard_category_%s' % hazard_category) # pylint: enable=eval-used item = QListWidgetItem(hazard_category['name'], self.lstHazardCategories) item.setData(QtCore.Qt.UserRole, unicode(hazard_category)) self.lstHazardCategories.addItem(item) # Set values based on existing keywords (if already assigned) category_keyword = self.get_existing_keyword('hazard_category') if category_keyword: categories = [] for index in xrange(self.lstHazardCategories.count()): item = self.lstHazardCategories.item(index) # pylint: disable=eval-used hazard_category = eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used categories.append(hazard_category['key']) if category_keyword in categories: self.lstHazardCategories.setCurrentRow( categories.index(category_keyword)) self.auto_select_one_item(self.lstHazardCategories) # =========================== # STEP_KW_LAYERMODE # =========================== # noinspection PyPep8Naming
[docs] def on_lstLayerModes_itemSelectionChanged(self): """Update layer mode description label and unit widgets. .. note:: This is an automatic Qt slot executed when the subcategory selection changes. """ # Clear all further steps in order to properly calculate the prev step self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets layer_mode = self.selected_layermode() # Exit if no selection if not layer_mode: self.lblDescribeLayerMode.setText('') return # Set description label self.lblDescribeLayerMode.setText(layer_mode['description']) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_layermode(self): """Obtain the layer mode selected by user. :returns: selected layer mode. :rtype: string, None """ item = self.lstLayerModes.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_layermode(self): """Set widgets on the LayerMode tab.""" # Clear all further steps in order to properly calculate the prev step self.lstUnits.clear() self.lstFields.clear() self.lstClassifications.clear() # Set widgets category = self.selected_category() subcategory = self.selected_subcategory() layer_mode_question = ( layermode_raster_question if is_raster_layer(self.layer) else layermode_vector_question) self.lblSelectLayerMode.setText( layer_mode_question % (subcategory['name'], category['name'])) self.lblDescribeLayerMode.setText('') self.lstLayerModes.clear() self.lstUnits.clear() self.lstFields.clear() layer_modes = self.layermodes_for_layer() for layer_mode in layer_modes: item = QListWidgetItem(layer_mode['name'], self.lstLayerModes) item.setData(QtCore.Qt.UserRole, unicode(layer_mode)) self.lstUnits.addItem(item) # Set value to existing keyword or default value layermode_keys = [m['key'] for m in layer_modes] layermode_keyword = self.get_existing_keyword('layer_mode') if layermode_keyword in layermode_keys: indx = layermode_keys.index(layermode_keyword) elif layer_mode_continuous['key'] in layermode_keys: # Set default value indx = layermode_keys.index(layer_mode_continuous['key']) else: indx = -1 self.lstLayerModes.setCurrentRow(indx) self.auto_select_one_item(self.lstLayerModes) # =========================== # STEP_KW_UNIT # =========================== # noinspection PyPep8Naming
[docs] def on_lstUnits_itemSelectionChanged(self): """Update unit description label and field widgets. .. note:: This is an automatic Qt slot executed when the unit selection changes. """ # Clear all further steps in order to properly calculate the prev step self.lstFields.clear() self.lstClassifications.clear() # Set widgets unit = self.selected_unit() # Exit if no selection if not unit: return self.lblDescribeUnit.setText(unit['description']) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_unit(self): """Obtain the unit selected by user. :returns: Metadata of the selected unit. :rtype: dict, None """ item = self.lstUnits.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_unit(self): """Set widgets on the Unit tab.""" # Clear all further steps in order to properly calculate the prev step self.lstFields.clear() self.lstClassifications.clear() # Set widgets category = self.selected_category() subcategory = self.selected_subcategory() self.lblSelectUnit.setText( unit_question % (subcategory['name'], category['name'])) self.lblDescribeUnit.setText('') self.lstUnits.clear() subcat = self.selected_subcategory()['key'] laygeo = self.get_layer_geometry_id() laymod = self.selected_layermode()['key'] if category == layer_purpose_hazard: hazcat = self.selected_hazard_category()['key'] units_for_layer = self.impact_function_manager.\ continuous_hazards_units_for_layer( subcat, laygeo, laymod, hazcat) else: units_for_layer = self.impact_function_manager\ .exposure_units_for_layer( subcat, laygeo, laymod) for unit_for_layer in units_for_layer: # if (self.get_layer_geometry_id() == 'raster' and # 'constraint' in unit_for_layer and # unit_for_layer['constraint'] == 'categorical'): # continue # else: item = QListWidgetItem(unit_for_layer['name'], self.lstUnits) item.setData(QtCore.Qt.UserRole, unicode(unit_for_layer)) self.lstUnits.addItem(item) # Set values based on existing keywords (if already assigned) if self.selected_category() == layer_purpose_hazard: key = continuous_hazard_unit['key'] else: key = exposure_unit['key'] unit_id = self.get_existing_keyword(key) # unit_id = definitions.old_to_new_unit_id(unit_id) if unit_id: units = [] for index in xrange(self.lstUnits.count()): item = self.lstUnits.item(index) # pylint: disable=eval-used unit = eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used units.append(unit['key']) if unit_id in units: self.lstUnits.setCurrentRow(units.index(unit_id)) self.auto_select_one_item(self.lstUnits) # =========================== # STEP_KW_CLASSIFICATION # ===========================
[docs] def on_lstClassifications_itemSelectionChanged(self): """Update classification description label and unlock the Next button. .. note:: This is an automatic Qt slot executed when the field selection changes. """ self.lstFields.clear() self.treeClasses.clear() classification = self.selected_classification() # Exit if no selection if not classification: return # Set description label self.lblDescribeClassification.setText(classification["description"]) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_classification(self): """Obtain the classification selected by user. :returns: Metadata of the selected classification. :rtype: dict, None """ item = self.lstClassifications.currentItem() try: # pylint: disable=eval-used return eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used except (AttributeError, NameError): return None
[docs] def set_widgets_step_kw_classification(self): """Set widgets on the Classification tab.""" self.lstFields.clear() self.treeClasses.clear() category = self.selected_category()['name'] subcategory = self.selected_subcategory()['name'] self.lstClassifications.clear() self.lblDescribeClassification.setText('') self.lblSelectClassification.setText( classification_question % (subcategory, category)) classifications = self.classifications_for_layer() for classification in classifications: if not isinstance(classification, dict): # pylint: disable=eval-used classification = eval('definitions.%s' % classification) # pylint: enable=eval-used item = QListWidgetItem(classification['name'], self.lstClassifications) item.setData(QtCore.Qt.UserRole, unicode(classification)) self.lstClassifications.addItem(item) # Set values based on existing keywords (if already assigned) geom = 'raster' if is_raster_layer(self.layer) else 'vector' key = '%s_%s_classification' % (geom, self.selected_category()['key']) classification_keyword = self.get_existing_keyword(key) if classification_keyword: classifications = [] for index in xrange(self.lstClassifications.count()): item = self.lstClassifications.item(index) # pylint: disable=eval-used classification = eval(item.data(QtCore.Qt.UserRole)) # pylint: enable=eval-used classifications.append(classification['key']) if classification_keyword in classifications: self.lstClassifications.setCurrentRow( classifications.index(classification_keyword)) self.auto_select_one_item(self.lstClassifications) # =========================== # STEP_KW_FIELD # =========================== # noinspection PyPep8Naming
[docs] def on_lstFields_itemSelectionChanged(self): """Update field description label and unlock the Next button. .. note:: This is an automatic Qt slot executed when the field selection changes. """ self.treeClasses.clear() field = self.selected_field() # Exit if no selection if not field: return fields = self.layer.dataProvider().fields() field_type = fields.field(field).typeName() field_index = fields.indexFromName(self.selected_field()) unique_values = self.layer.uniqueValues(field_index)[0:48] unique_values_str = [i is not None and unicode(i) or 'NULL' for i in unique_values] if unique_values != self.layer.uniqueValues(field_index): unique_values_str += ['...'] desc = '<br/>%s: %s<br/><br/>' % (self.tr('Field type'), field_type) desc += self.tr('Unique values: %s') % ', '.join(unique_values_str) self.lblDescribeField.setText(desc) # Enable the next button self.pbnNext.setEnabled(True)
[docs] def selected_field(self): """Obtain the field selected by user. :returns: Keyword of the selected field. :rtype: string, None """ item = self.lstFields.currentItem() if item: return item.text() else: return None
[docs] def set_widgets_step_kw_field(self): """Set widgets on the Field tab.""" self.treeClasses.clear() category = self.selected_category() subcategory = self.selected_subcategory() unit = self.selected_unit() if category == layer_purpose_aggregation: question_text = field_question_aggregation elif self.selected_layermode() == layer_mode_continuous and unit: # unique values, continuous or categorical data subcategory_unit_relation = get_question_text( '%s_%s_question' % (subcategory['key'], unit['key'])) question_text = field_question_subcategory_unit % ( category['name'], subcategory['name'], unit['name'], subcategory_unit_relation) else: question_text = field_question_subcategory_classified % ( subcategory['name']) self.lblSelectField.setText(question_text) self.lstFields.clear() default_item = None for field in self.layer.dataProvider().fields(): field_name = field.name() item = QListWidgetItem(field_name, self.lstFields) item.setData(QtCore.Qt.UserRole, field_name) # Select the item if it match the unit's default_attribute if unit and 'default_attribute' in unit \ and field_name == unit['default_attribute']: default_item = item # For continuous data, gray out id, gid, fid and text fields if self.selected_layermode() == layer_mode_continuous and unit: field_type = field.type() if field_type > 9 or re.match( '.{0,2}id$', field_name, re.I): item.setFlags(item.flags() & ~QtCore.Qt.ItemIsEnabled) if default_item: self.lstFields.setCurrentItem(default_item) self.lblDescribeField.clear() # Set values based on existing keywords (if already assigned) field_keyword = self.field_keyword_for_the_layer() field = self.get_existing_keyword(field_keyword) if field: fields = [] for index in xrange(self.lstFields.count()): fields.append(str(self.lstFields.item(index).text())) if field in fields: self.lstFields.setCurrentRow(fields.index(field)) self.auto_select_one_item(self.lstFields) # =========================== # STEP_KW_RESAMPLE # ===========================
[docs] def selected_allowresampling(self): """Obtain the allow_resampling state selected by user. .. note:: Returns none if not set or not relevant :returns: Value of the allow_resampling or None for not-set. :rtype: boolean or None """ if not is_raster_layer(self.layer): return None if self.selected_category() != layer_purpose_exposure: return None # Only return false if checked, otherwise None for not-set. if self.chkAllowResample.isChecked(): return False else: return None
[docs] def set_widgets_step_kw_resample(self): """Set widgets on the Resample tab.""" category = self.selected_category() subcategory = self.selected_subcategory() layer_mode = self.selected_layermode() self.lblSelectAllowResample.setText( allow_resampling_question % ( subcategory['name'], category['name'], layer_mode['name'])) # Set value based on existing keyword (if already assigned) if self.get_existing_keyword('allow_resampling') is False: self.chkAllowResample.setChecked(True) # =========================== # STEP_KW_CLASSIFY # =========================== # noinspection PyMethodMayBeStatic
[docs] def update_dragged_item_flags(self, item, column): """Fix the drop flag after the item is dropped. Check if it looks like an item dragged from QListWidget to QTreeWidget and disable the drop flag. For some reasons the flag is set when dragging. :param item: :param column: .. note:: This is a slot executed when the item change. """ # Treat var as unused _ = column if int(item.flags() & QtCore.Qt.ItemIsDropEnabled) \ and int(item.flags() & QtCore.Qt.ItemIsDragEnabled): item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled)
[docs] def selected_mapping(self): """Obtain the value-to-class mapping set by user. :returns: The complete mapping as a dict of lists. :rtype: dict """ value_map = {} tree_clone = self.treeClasses.invisibleRootItem().clone() for tree_branch in tree_clone.takeChildren(): value_list = [] for tree_leaf in tree_branch.takeChildren(): value_list += [tree_leaf.data(0, QtCore.Qt.UserRole)] if value_list: value_map[tree_branch.text(0)] = value_list return value_map
[docs] def set_widgets_step_kw_classify(self): """Set widgets on the Classify tab.""" category = self.selected_category() subcategory = self.selected_subcategory() classification = self.selected_classification() default_classes = classification['classes'] if is_raster_layer(self.layer): self.lblClassify.setText(classify_raster_question % ( subcategory['name'], category['name'], classification['name'])) ds = gdal.Open(self.layer.source(), GA_ReadOnly) unique_values = numpy.unique(numpy.array( ds.GetRasterBand(1).ReadAsArray())) field_type = 0 else: field = self.selected_field() field_index = self.layer.dataProvider().fields().indexFromName( self.selected_field()) field_type = self.layer.dataProvider().fields()[field_index].type() self.lblClassify.setText(classify_vector_question % ( subcategory['name'], category['name'], classification['name'], field.upper())) unique_values = self.layer.uniqueValues(field_index) # Assign unique values to classes (according to defauls) unassigned_values = list() assigned_values = dict() for default_class in default_classes: assigned_values[default_class['name']] = list() for unique_value in unique_values: if unique_value is None or isinstance( unique_value, QPyNullVariant): # Don't classify features with NULL value continue value_as_string = unicode(unique_value) assigned = False for default_class in default_classes: condition_1 = ( field_type > 9 and value_as_string.upper() in [ c.upper() for c in default_class['string_defaults']]) condition_2 = ( field_type < 10 and ( default_class['numeric_default_min'] <= unique_value <= default_class['numeric_default_max'])) if condition_1 or condition_2: assigned_values[default_class['name']] += [unique_value] assigned = True if not assigned: # add to unassigned values list otherwise unassigned_values += [unique_value] self.populate_classified_values( unassigned_values, assigned_values, default_classes) # Overwrite assigned values according to existing keyword (if present). # Note the default_classes and unique_values are already loaded! value_map = self.get_existing_keyword('value_map') # Do not continue if there is no value_map in existing keywords if value_map is None: return # Do not continue if user selected different field field_keyword = self.field_keyword_for_the_layer() field = self.get_existing_keyword(field_keyword) if not is_raster_layer(self.layer) and field != self.selected_field(): return unassigned_values = list() assigned_values = dict() for default_class in default_classes: assigned_values[default_class['name']] = list() if isinstance(value_map, str): try: value_map = json.loads(value_map) except ValueError: return for unique_value in unique_values: if unique_value is None or isinstance( unique_value, QPyNullVariant): # Don't classify features with NULL value continue # check in value map assigned = False for key, value_list in value_map.iteritems(): if unique_value in value_list and key in assigned_values: assigned_values[key] += [unique_value] assigned = True if not assigned: unassigned_values += [unique_value] self.populate_classified_values( unassigned_values, assigned_values, default_classes)
[docs] def populate_classified_values( self, unassigned_values, assigned_values, default_classes): """Populate lstUniqueValues and treeClasses.from the parameters. :param unassigned_values: List of values that haven't been assigned to a class. It will be put in self.lstUniqueValues. :type unassigned_values: list :param assigned_values: Dictionary with class as the key and list of value as the value of the dictionary. It will be put in self.treeClasses. :type assigned_values: dict :param default_classes: Default classes from unit. :type default_classes: list """ # Populate the unique values list self.lstUniqueValues.clear() for value in unassigned_values: value_as_string = value is not None and unicode(value) or 'NULL' list_item = QtGui.QListWidgetItem(self.lstUniqueValues) list_item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled) list_item.setData(QtCore.Qt.UserRole, value) list_item.setText(value_as_string) self.lstUniqueValues.addItem(list_item) # Populate assigned values tree self.treeClasses.clear() bold_font = QtGui.QFont() bold_font.setItalic(True) bold_font.setBold(True) bold_font.setWeight(75) self.treeClasses.invisibleRootItem().setFlags( QtCore.Qt.ItemIsEnabled) for default_class in default_classes: # Create branch for class tree_branch = QtGui.QTreeWidgetItem(self.treeClasses) tree_branch.setFlags(QtCore.Qt.ItemIsDropEnabled | QtCore.Qt.ItemIsEnabled) tree_branch.setExpanded(True) tree_branch.setFont(0, bold_font) tree_branch.setText(0, default_class['name']) if 'description' in default_class: tree_branch.setToolTip(0, default_class['description']) # Assign known values for value in assigned_values[default_class['name']]: string_value = value is not None and unicode(value) or 'NULL' tree_leaf = QtGui.QTreeWidgetItem(tree_branch) tree_leaf.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsDragEnabled) tree_leaf.setData(0, QtCore.Qt.UserRole, value) tree_leaf.setText(0, string_value) # =========================== # STEP_KW_EXTRAKEYWORDS # =========================== # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword1_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 1st extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[0]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword2_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 2nd extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[1]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword3_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 3rd extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[2]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword4_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 4th extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[3]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword5_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 5th extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[4]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword6_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 6th extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[5]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword7_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 7th extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[6]) # noinspection PyPep8Naming
[docs] def on_cboExtraKeyword8_currentIndexChanged(self, indx): """This is an automatic Qt slot executed when the 8th extra keyword combobox selection changes. :param indx: The new index. :type indx: int or str """ if isinstance(indx, int) and indx > -1: self.extra_keyword_changed(self.extra_keywords_widgets[7])
[docs] def extra_keyword_changed(self, widget): """Populate slave widget if exists and enable the Next button if all extra keywords are set. :param widget: Metadata of the widget where the event happened. :type widget: dict """ if 'slave_key' in widget and widget['slave_key']: for w in self.extra_keywords_widgets: if w['key'] == widget['slave_key']: field_name = widget['cbo'].itemData( widget['cbo'].currentIndex(), QtCore.Qt.UserRole) self.populate_value_widget_from_field(w['cbo'], field_name) self.pbnNext.setEnabled(self.are_all_extra_keywords_selected())
[docs] def selected_extra_keywords(self): """Obtain the extra keywords selected by user. :returns: Metadata of the extra keywords. :rtype: dict, None """ extra_keywords = {} for ekw in self.extra_keywords_widgets: if ekw['key'] is not None and ekw['cbo'].currentIndex() != -1: key = ekw['key'] val = ekw['cbo'].itemData(ekw['cbo'].currentIndex(), QtCore.Qt.UserRole) extra_keywords[key] = val return extra_keywords
[docs] def are_all_extra_keywords_selected(self): """Ensure all all additional keyword are set by user :returns: True if all additional keyword widgets are set :rtype: boolean """ for ekw in self.extra_keywords_widgets: if ekw['key'] is not None and ekw['cbo'].currentIndex() == -1: return False return True
[docs] def populate_value_widget_from_field(self, widget, field_name): """Populate the slave widget with unique values of the field selected in the master widget. :param widget: The widget to be populated :type widget: QComboBox :param field_name: Name of the field to take the values from :type field_name: str """ fields = self.layer.dataProvider().fields() field_index = fields.indexFromName(field_name) widget.clear() for v in self.layer.uniqueValues(field_index): widget.addItem(unicode(v), unicode(v)) widget.setCurrentIndex(-1)
[docs] def set_widgets_step_kw_extrakeywords(self): """Set widgets on the Extra Keywords tab.""" # Hide all widgets for ekw in self.extra_keywords_widgets: ekw['cbo'].clear() ekw['cbo'].hide() ekw['lbl'].hide() ekw['key'] = None ekw['master_key'] = None # Set and show used widgets extra_keywords = self.additional_keywords_for_the_layer() for i in range(len(extra_keywords)): extra_keyword = extra_keywords[i] extra_keywords_widget = self.extra_keywords_widgets[i] extra_keywords_widget['key'] = extra_keyword['key'] extra_keywords_widget['lbl'].setText(extra_keyword['description']) if extra_keyword['type'] == 'value': field_widget = self.extra_keywords_widgets[i - 1]['cbo'] field_name = field_widget.itemData( field_widget.currentIndex(), QtCore.Qt.UserRole) self.populate_value_widget_from_field( extra_keywords_widget['cbo'], field_name) else: for field in self.layer.dataProvider().fields(): field_name = field.name() field_type = field.typeName() extra_keywords_widget['cbo'].addItem('%s (%s)' % ( field_name, field_type), field_name) # If there is a master keyword, attach this widget as a slave # to the master widget. It's used for values of a given field. if ('master_keyword' in extra_keyword and extra_keyword['master_keyword']): master_key = extra_keyword['master_keyword']['key'] for master_candidate in self.extra_keywords_widgets: if master_candidate['key'] == master_key: master_candidate['slave_key'] = extra_keyword['key'] # Show the widget extra_keywords_widget['cbo'].setCurrentIndex(-1) extra_keywords_widget['lbl'].show() extra_keywords_widget['cbo'].show() # Set values based on existing keywords (if already assigned) for ekw in self.extra_keywords_widgets: if not ekw['key']: continue value = self.get_existing_keyword(ekw['key']) indx = ekw['cbo'].findData(value, QtCore.Qt.UserRole) if indx != -1: ekw['cbo'].setCurrentIndex(indx) # =========================== # STEP_KW_AGGREGATION # =========================== # noinspection PyPep8Naming,PyMethodMayBeStatic
[docs] def on_cboFemaleRatioAttribute_currentIndexChanged(self): """Automatic slot executed when the female ratio attribute is changed. When the user changes the female ratio attribute (cboFemaleRatioAttribute), it will change the enabled value of dsbFemaleRatioDefault. If value is 'Use default', enable dsbFemaleRatioDefault. Otherwise, disabled it. """ value = self.cboFemaleRatioAttribute.currentText() if value == self.global_default_string: self.dsbFemaleRatioDefault.setEnabled(True) else: self.dsbFemaleRatioDefault.setEnabled(False) # noinspection PyPep8Naming,PyMethodMayBeStatic
[docs] def on_cboYouthRatioAttribute_currentIndexChanged(self): """Automatic slot executed when the youth ratio attribute is changed. When the user changes the youth ratio attribute (cboYouthRatioAttribute), it will change the enabled value of dsbYouthRatioDefault. If value is 'Use default', enable dsbYouthRatioDefault. Otherwise, disabled it. """ value = self.cboYouthRatioAttribute.currentText() if value == self.global_default_string: self.dsbYouthRatioDefault.setEnabled(True) else: self.dsbYouthRatioDefault.setEnabled(False) # noinspection PyPep8Naming,PyMethodMayBeStatic
[docs] def on_cboAdultRatioAttribute_currentIndexChanged(self): """Automatic slot executed when the adult ratio attribute is changed. When the user changes the adult ratio attribute (cboAdultRatioAttribute), it will change the enabled value of dsbAdultRatioDefault. If value is 'Use default', enable dsbAdultRatioDefault. Otherwise, disabled it. """ value = self.cboAdultRatioAttribute.currentText() if value == self.global_default_string: self.dsbAdultRatioDefault.setEnabled(True) else: self.dsbAdultRatioDefault.setEnabled(False) # noinspection PyPep8Naming,PyMethodMayBeStatic
[docs] def on_cboElderlyRatioAttribute_currentIndexChanged(self): """Automatic slot executed when the adult ratio attribute is changed. When the user changes the elderly ratio attribute (cboElderlyRatioAttribute), it will change the enabled value of dsbElderlyRatioDefault. If value is 'Use default', enable dsbElderlyRatioDefault. Otherwise, disabled it. """ value = self.cboElderlyRatioAttribute.currentText() if value == self.global_default_string: self.dsbElderlyRatioDefault.setEnabled(True) else: self.dsbElderlyRatioDefault.setEnabled(False)
[docs] def get_aggregation_attributes(self): """Obtain the value of aggregation attributes set by user. :returns: The key and value of aggregation attributes. :rtype: dict """ aggregation_attributes = dict() current_index = self.cboFemaleRatioAttribute.currentIndex() data = self.cboFemaleRatioAttribute.itemData(current_index) aggregation_attributes[female_ratio_attribute_key] = data value = self.dsbFemaleRatioDefault.value() aggregation_attributes[female_ratio_default_key] = value current_index = self.cboYouthRatioAttribute.currentIndex() data = self.cboYouthRatioAttribute.itemData(current_index) aggregation_attributes[youth_ratio_attribute_key] = data value = self.dsbYouthRatioDefault.value() aggregation_attributes[youth_ratio_default_key] = value current_index = self.cboAdultRatioAttribute.currentIndex() data = self.cboAdultRatioAttribute.itemData(current_index) aggregation_attributes[adult_ratio_attribute_key] = data value = self.dsbAdultRatioDefault.value() aggregation_attributes[adult_ratio_default_key] = value current_index = self.cboElderlyRatioAttribute.currentIndex() data = self.cboElderlyRatioAttribute.itemData(current_index) aggregation_attributes[elderly_ratio_attribute_key] = data value = self.dsbElderlyRatioDefault.value() aggregation_attributes[elderly_ratio_default_key] = value return aggregation_attributes
[docs] def age_ratios_are_valid(self): """Return true if the sum of age ratios is good, otherwise False. Good means their sum does not exceed 1. :returns: Tuple of boolean and float. Boolean represent good or not good, while float represent the summation of age ratio. If some ratio do not use global default, the summation is set to 0. :rtype: tuple """ youth_ratio_index = self.cboYouthRatioAttribute.currentIndex() adult_ratio_index = self.cboAdultRatioAttribute.currentIndex() elderly_ratio_index = self.cboElderlyRatioAttribute.currentIndex() ratio_indexes = [ youth_ratio_index, adult_ratio_index, elderly_ratio_index] if ratio_indexes.count(0) == len(ratio_indexes): youth_ratio_default = self.dsbYouthRatioDefault.value() adult_ratio_default = self.dsbAdultRatioDefault.value() elderly_ratio_default = self.dsbElderlyRatioDefault.value() sum_ratio_default = youth_ratio_default + adult_ratio_default sum_ratio_default += elderly_ratio_default if sum_ratio_default > 1: return False, sum_ratio_default else: return True, sum_ratio_default return True, 0 # noinspection PyUnresolvedReferences,PyStatementEffect
[docs] def populate_cbo_aggregation_attribute( self, ratio_attribute_key, cbo_ratio_attribute): """Populate the combo box cbo_ratio_attribute for ratio_attribute_key. :param ratio_attribute_key: A ratio attribute key that saved in keywords. :type ratio_attribute_key: str :param cbo_ratio_attribute: A combo box that wants to be populated. :type cbo_ratio_attribute: QComboBox """ cbo_ratio_attribute.clear() ratio_attribute = self.get_existing_keyword(ratio_attribute_key) fields, attribute_position = layer_attribute_names( self.layer, [QtCore.QVariant.Double], ratio_attribute) cbo_ratio_attribute.addItem( self.global_default_string, self.global_default_data) cbo_ratio_attribute.addItem( self.do_not_use_string, self.do_not_use_data) for field in fields: cbo_ratio_attribute.addItem(field, field) # For backward compatibility, still use Use default if (ratio_attribute == self.global_default_data or ratio_attribute == self.tr('Use default')): cbo_ratio_attribute.setCurrentIndex(0) elif ratio_attribute == self.do_not_use_data: cbo_ratio_attribute.setCurrentIndex(1) elif ratio_attribute is None or attribute_position is None: # current_keyword was not found in the attribute table. # Use default cbo_ratio_attribute.setCurrentIndex(0) else: # + 2 is because we add use defaults and don't use cbo_ratio_attribute.setCurrentIndex(attribute_position + 2)
[docs] def set_widgets_step_kw_aggregation(self): """Set widgets on the aggregation tab.""" # Set values based on existing keywords (if already assigned) self.defaults = get_defaults() female_ratio_default = self.get_existing_keyword( female_ratio_default_key) if female_ratio_default: self.dsbFemaleRatioDefault.setValue( float(female_ratio_default)) else: self.dsbFemaleRatioDefault.setValue(self.defaults['FEMALE_RATIO']) youth_ratio_default = self.get_existing_keyword( youth_ratio_default_key) if youth_ratio_default: self.dsbYouthRatioDefault.setValue(float(youth_ratio_default)) else: self.dsbYouthRatioDefault.setValue(self.defaults['YOUTH_RATIO']) adult_ratio_default = self.get_existing_keyword( adult_ratio_default_key) if adult_ratio_default: self.dsbAdultRatioDefault.setValue(float(adult_ratio_default)) else: self.dsbAdultRatioDefault.setValue(self.defaults['ADULT_RATIO']) elderly_ratio_default = self.get_existing_keyword( elderly_ratio_default_key) if elderly_ratio_default: self.dsbElderlyRatioDefault.setValue(float(elderly_ratio_default)) else: self.dsbElderlyRatioDefault.setValue( self.defaults['ELDERLY_RATIO']) ratio_attribute_keys = [ female_ratio_attribute_key, youth_ratio_attribute_key, adult_ratio_attribute_key, elderly_ratio_attribute_key] cbo_ratio_attributes = [ self.cboFemaleRatioAttribute, self.cboYouthRatioAttribute, self.cboAdultRatioAttribute, self.cboElderlyRatioAttribute] for i in range(len(cbo_ratio_attributes)): self.populate_cbo_aggregation_attribute( ratio_attribute_keys[i], cbo_ratio_attributes[i]) # =========================== # STEP_KW_SOURCE # ===========================
[docs] def set_widgets_step_kw_source(self): """Set widgets on the Source tab.""" # Just set values based on existing keywords source = self.get_existing_keyword('source') if source or source == 0: self.leSource.setText(get_unicode(source)) source_scale = self.get_existing_keyword('scale') if source_scale or source_scale == 0: self.leSource_scale.setText(get_unicode(source_scale)) source_date = self.get_existing_keyword('date') if source_date or source_date == 0: self.leSource_date.setText(get_unicode(source_date)) source_url = self.get_existing_keyword('url') if source_url or source_url == 0: self.leSource_url.setText(get_unicode(source_url)) source_license = self.get_existing_keyword('license') if source_license or source_license == 0: self.leSource_license.setText(get_unicode(source_license)) # =========================== # STEP_KW_TITLE # =========================== # noinspection PyPep8Naming
[docs] def on_leTitle_textChanged(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the title value changes. """ self.pbnNext.setEnabled(bool(self.leTitle.text()))
[docs] def set_widgets_step_kw_title(self): """Set widgets on the Title tab.""" # Just set values based on existing keywords if self.layer: title = self.layer.name() self.leTitle.setText(title) # =========================== # STEP_FC_FUNCTION_1 # ===========================
[docs] def selected_functions_1(self): """Obtain functions available for hazard an exposure selected by user. :returns: List of the available functions metadata. :rtype: list, None """ selection = self.tblFunctions1.selectedItems() if len(selection) != 1: return [] try: return selection[0].data(RoleFunctions) except (AttributeError, NameError): return None
[docs] def selected_impact_function_constraints(self): """Obtain impact function constraints selected by user. :returns: Tuple of metadata of hazard, exposure, hazard layer constraints and exposure layer constraints :rtype: tuple """ selection = self.tblFunctions1.selectedItems() if len(selection) != 1: return None, None, None, None h = selection[0].data(RoleHazard) e = selection[0].data(RoleExposure) selection = self.tblFunctions2.selectedItems() if len(selection) != 1: return h, e, None, None hc = selection[0].data(RoleHazardConstraint) ec = selection[0].data(RoleExposureConstraint) return h, e, hc, ec # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_tblFunctions1_itemSelectionChanged(self): """Choose selected hazard x exposure combination. .. note:: This is an automatic Qt slot executed when the category selection changes. """ functions = self.selected_functions_1() if not functions: self.lblAvailableFunctions1.clear() else: txt = self.tr('Available functions:') + ' ' + ', '.join( [f['name'] for f in functions]) self.lblAvailableFunctions1.setText(txt) # Clear the selection on the 2nd matrix self.tblFunctions2.clearContents() self.lblAvailableFunctions2.clear() self.pbnNext.setEnabled(True) # Put a dot to the selected cell - note there is no way # to center an icon without using a custom ItemDelegate selection = self.tblFunctions1.selectedItems() selItem = (len(selection) == 1) and selection[0] or None for row in range(self.tblFunctions1.rowCount()): for col in range(self.tblFunctions1.columnCount()): item = self.tblFunctions1.item(row, col) item.setText((item == selItem) and u'\u2022' or '') # pylint: disable=W0613 # noinspection PyPep8Naming
[docs] def on_tblFunctions1_cellDoubleClicked(self, row, column): """Choose selected hazard x exposure combination and go ahead. .. note:: This is an automatic Qt slot executed when the category selection changes. """ self.pbnNext.click() # pylint: enable=W0613
[docs] def populate_function_table_1(self): """Populate the tblFunctions1 table with available functions.""" # The hazard category radio buttons are now removed - # make this parameter of IFM.available_hazards() optional hazard_category = hazard_category_single_event hazards = self.impact_function_manager\ .available_hazards(hazard_category['key']) # Remove 'generic' from hazards for h in hazards: if h['key'] == 'generic': hazards.remove(h) exposures = self.impact_function_manager.available_exposures() self.lblAvailableFunctions1.clear() self.tblFunctions1.clear() self.tblFunctions1.setColumnCount(len(hazards)) self.tblFunctions1.setRowCount(len(exposures)) for i in range(len(hazards)): h = hazards[i] item = QtGui.QTableWidgetItem() item.setIcon(QtGui.QIcon( resources_path('img', 'wizard', 'keyword-subcategory-%s.svg' % (h['key'] or 'notset')))) item.setText(h['name'].capitalize()) self.tblFunctions1.setHorizontalHeaderItem(i, item) for i in range(len(exposures)): e = exposures[i] item = QtGui.QTableWidgetItem() item.setIcon(QtGui.QIcon( resources_path('img', 'wizard', 'keyword-subcategory-%s.svg' % (e['key'] or 'notset')))) item.setText(e['name'].capitalize()) self.tblFunctions1.setVerticalHeaderItem(i, item) big_font = QtGui.QFont() big_font.setPointSize(80) for h in hazards: for e in exposures: item = QtGui.QTableWidgetItem() functions = \ self.impact_function_manager.functions_for_constraint( h['key'], e['key']) if len(functions): background_colour = QtGui.QColor(120, 255, 120) else: background_colour = QtGui.QColor(220, 220, 220) item.setFlags(item.flags() & ~QtCore.Qt.ItemIsEnabled) item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable) item.setBackground(QtGui.QBrush(background_colour)) item.setFont(big_font) item.setTextAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter) item.setData(RoleFunctions, functions) item.setData(RoleHazard, h) item.setData(RoleExposure, e) self.tblFunctions1.setItem( exposures.index(e), hazards.index(h), item) self.pbnNext.setEnabled(False)
[docs] def set_widgets_step_fc_function_1(self): """Set widgets on the Impact Functions Table 1 tab.""" self.tblFunctions1.horizontalHeader().setResizeMode( QtGui.QHeaderView.Stretch) self.tblFunctions1.verticalHeader().setResizeMode( QtGui.QHeaderView.Stretch) self.populate_function_table_1() # =========================== # STEP_FC_FUNCTION_2 # ===========================
[docs] def selected_functions_2(self): """Obtain functions available for hazard and exposure selected by user. :returns: List of the available functions metadata. :rtype: list, None """ selection = self.tblFunctions2.selectedItems() if len(selection) != 1: return [] return selection[0].data(RoleFunctions) # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_tblFunctions2_itemSelectionChanged(self): """Choose selected hazard x exposure constraints combination. .. note:: This is an automatic Qt slot executed when the category selection changes. """ functions = self.selected_functions_2() if not functions: self.lblAvailableFunctions2.clear() else: text = self.tr('Available functions:') + ' ' + ', '.join( [f['name'] for f in functions]) self.lblAvailableFunctions2.setText(text) self.pbnNext.setEnabled(True) # Put a dot to the selected cell - note there is no way # to center an icon without using a custom ItemDelegate selection = self.tblFunctions2.selectedItems() selItem = (len(selection) == 1) and selection[0] or None for row in range(self.tblFunctions2.rowCount()): for col in range(self.tblFunctions2.columnCount()): item = self.tblFunctions2.item(row, col) item.setText((item == selItem) and u'\u2022' or '') # pylint: disable=W0613 # noinspection PyPep8Naming,PyUnusedLocal
[docs] def on_tblFunctions2_cellDoubleClicked(self, row, column): """Click handler for selecting hazard and exposure constraints. :param row: The row that the user clicked on. :type row: int :param column: The column that the user clicked on. :type column: int .. note:: This is an automatic Qt slot executed when the category selection changes. """ self.pbnNext.click() # pylint: enable=W0613
[docs] def set_widgets_step_fc_function_2(self): """Set widgets on the Impact Functions Table 2 tab.""" self.tblFunctions2.clear() h, e, _hc, _ec = self.selected_impact_function_constraints() hazard_layer_geometries = [ layer_geometry_raster, layer_geometry_point, layer_geometry_line, layer_geometry_polygon] exposure_layer_geometries = [ layer_geometry_raster, layer_geometry_point, layer_geometry_line, layer_geometry_polygon] self.lblSelectFunction2.setText( select_function_constraints2_question % (h['name'], e['name'])) self.tblFunctions2.setColumnCount(len(hazard_layer_geometries)) self.tblFunctions2.setRowCount(len(exposure_layer_geometries)) # self.tblFunctions2.setHorizontalHeaderLabels( # [i['layer_geometry'].capitalize() if i['layer_mode'] != 'raster' # else ('%s %s' % (i['layer_geometry'], # i['layer_mode'])).capitalize() # for i in hazard_layer_geometries]) # for i in range(len(exposure_layer_geometries)): # constr = exposure_layer_geometries[i] # item = QtGui.QTableWidgetItem() # if constr['layer_mode'] == 'raster': # text = '%s\n%s' % (constr['layer_geometry'], # constr['layer_mode']) # else: # text = constr['layer_geometry'] # item.setText(text.capitalize()) # item.setTextAlignment(QtCore.Qt.AlignCenter) # self.tblFunctions2.setVerticalHeaderItem(i, item) self.tblFunctions2.setHorizontalHeaderLabels( [i['name'].capitalize() for i in hazard_layer_geometries]) for i in range(len(exposure_layer_geometries)): item = QtGui.QTableWidgetItem() item.setText(exposure_layer_geometries[i]['name'].capitalize()) item.setTextAlignment(QtCore.Qt.AlignCenter) self.tblFunctions2.setVerticalHeaderItem(i, item) self.tblFunctions2.horizontalHeader().setResizeMode( QtGui.QHeaderView.Stretch) self.tblFunctions2.verticalHeader().setResizeMode( QtGui.QHeaderView.Stretch) big_font = QtGui.QFont() big_font.setPointSize(80) active_items = [] for col in range(len(hazard_layer_geometries)): for row in range(len(exposure_layer_geometries)): hc = hazard_layer_geometries[col] ec = exposure_layer_geometries[row] functions = self.impact_function_manager\ .functions_for_constraint( h['key'], e['key'], hc['key'], ec['key']) item = QtGui.QTableWidgetItem() if len(functions): bgcolor = QtGui.QColor(120, 255, 120) active_items += [item] else: bgcolor = QtGui.QColor(220, 220, 220) item.setFlags(item.flags() & ~QtCore.Qt.ItemIsEnabled) item.setFlags(item.flags() & ~QtCore.Qt.ItemIsSelectable) item.setBackground(QtGui.QBrush(bgcolor)) item.setFont(big_font) item.setTextAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter) item.setData(RoleFunctions, functions) item.setData(RoleHazard, h) item.setData(RoleExposure, e) item.setData(RoleHazardConstraint, hc) item.setData(RoleExposureConstraint, ec) self.tblFunctions2.setItem(row, col, item) # Automatically select one item... if len(active_items) == 1: active_items[0].setSelected(True) # set focus, as the inactive selection style is gray self.tblFunctions2.setFocus() # =========================== # STEP_FC_FUNCTION_3 # =========================== # noinspection PyPep8Naming
[docs] def on_lstFunctions_itemSelectionChanged(self): """Update function description label .. note:: This is an automatic Qt slot executed when the category selection changes. """ imfunc = self.selected_function() # Exit if no selection if not imfunc: self.lblDescribeFunction.clear() self.pbnNext.setEnabled(False) # Set the branch description if selected branch = self.selected_function_group() if branch and "description" in branch.keys(): self.lblDescribeFunction.setText(branch['description']) return # Set description label description = '<table border="0">' if "name" in imfunc.keys(): description += '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( self.tr('Function'), imfunc['name']) if "overview" in imfunc.keys(): description += '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( self.tr('Overview'), imfunc['overview']) description += '</table>' self.lblDescribeFunction.setText(description) # Enable the next button if anything selected self.pbnNext.setEnabled(bool(self.selected_function()))
[docs] def selected_function(self): """Obtain the impact function selected by user. :returns: metadata of the selected function. :rtype: dict, None """ item = self.lstFunctions.currentItem() if not item: return None data = item.data(QtCore.Qt.UserRole) if data: return data else: return None
[docs] def set_widgets_step_fc_function_3(self): """Set widgets on the Impact Functions tab.""" self.lstFunctions.clear() self.lblDescribeFunction.setText('') h, e, hc, ec = self.selected_impact_function_constraints() functions = self.impact_function_manager.functions_for_constraint( h['key'], e['key'], hc['key'], ec['key']) self.lblSelectFunction.setText( select_function_question % ( hc['name'], h['name'], ec['name'], e['name'])) for f in functions: item = QtGui.QListWidgetItem(self.lstFunctions) item.setText(f['name']) item.setData(QtCore.Qt.UserRole, f) self.auto_select_one_item(self.lstFunctions) # Set hazard and exposure icons on next steps icon_path = resources_path('img', 'wizard', 'keyword-subcategory-%s.svg' % (h['key'] or 'notset')) self.lblIconFunctionHazard.setPixmap(QPixmap(icon_path)) self.lblIconIFCWHazardOrigin.setPixmap(QPixmap(icon_path)) self.lblIconIFCWHazardFromCanvas.setPixmap(QPixmap(icon_path)) self.lblIconIFCWHazardFromBrowser.setPixmap(QPixmap(icon_path)) icon_path = resources_path('img', 'wizard', 'keyword-subcategory-%s.svg' % (e['key'] or 'notset')) self.lblIconFunctionExposure.setPixmap(QPixmap(icon_path)) self.lblIconIFCWExposureOrigin.setPixmap(QPixmap(icon_path)) self.lblIconIFCWExposureFromCanvas.setPixmap(QPixmap(icon_path)) self.lblIconIFCWExposureFromBrowser.setPixmap(QPixmap(icon_path)) icon_path = resources_path('img', 'wizard', 'keyword-category-aggregation.svg') # Temporarily hide aggregation icon until we have one suitable # (as requested in a comment to PR #2060) icon_path = None self.lblIconIFCWAggregationOrigin.setPixmap(QPixmap(icon_path)) self.lblIconIFCWAggregationFromCanvas.setPixmap(QPixmap(icon_path)) self.lblIconIFCWAggregationFromBrowser.setPixmap(QPixmap(icon_path)) # =========================== # STEP_FC_HAZLAYER_ORIGIN # =========================== # noinspection PyPep8Naming
[docs] def on_rbHazLayerFromCanvas_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbHazLayerFromBrowser_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True)
[docs] def is_layer_compatible(self, layer, layer_purpose, 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: string :param keywords: The layer keywords :type keywords: None, dict :returns: True if layer is appropriate for the selected role :rtype: boolean """ # 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 compare_version(keyword_version, get_version()) != 0: 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.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
[docs] def get_compatible_layers_from_canvas(self, category): """Collect compatible layers from map canvas. .. note:: Returns layers with keywords and layermode matching the category and compatible with the selected impact function. Also returns layers without keywords with layermode compatible with the selected impact function. :param category: The category to filter for. :type category: string :returns: Metadata of found layers. :rtype: list of dicts """ # Collect compatible layers layers = [] for layer in self.iface.mapCanvas().layers(): 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 if self.is_layer_compatible(layer, category, keywords): layers += [ {'id': layer.id(), 'name': layer.name(), 'keywords': keywords}] # Move layers without keywords to the end l1 = [l for l in layers if l['keywords']] l2 = [l for l in layers if not l['keywords']] layers = l1 + l2 return layers
[docs] def list_compatible_layers_from_canvas(self, category, list_widget): """Fill given list widget with compatible layers. .. note:: Uses get_compatible_layers_from_canvas() to filter layers :param category: The category to filter for. :type category: string :param list_widget: The list widget to be filled with layers. :type list_widget: QListWidget :returns: Metadata of found layers. :rtype: list of dicts """ italic_font = QtGui.QFont() italic_font.setItalic(True) # Add compatible layers list_widget.clear() for layer in self.get_compatible_layers_from_canvas(category): item = QListWidgetItem(layer['name'], list_widget) item.setData(QtCore.Qt.UserRole, layer['id']) if not layer['keywords']: item.setFont(italic_font) list_widget.addItem(item)
[docs] def set_widgets_step_fc_hazlayer_origin(self): """Set widgets on the Hazard 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_hazlayer_from_canvas because we need # to list them again after coming back from the Keyword Wizard. self.list_compatible_layers_from_canvas( 'hazard', self.lstCanvasHazLayers) if self.lstCanvasHazLayers.count(): self.rbHazLayerFromCanvas.setText(tr( 'I would like to use a hazard layer already loaded in QGIS\n' '(launches the %s for hazard if needed)' ) % self.keyword_creation_wizard_name) self.rbHazLayerFromCanvas.setEnabled(True) self.rbHazLayerFromCanvas.click() else: self.rbHazLayerFromCanvas.setText(tr( 'I would like to use a hazard layer already loaded in QGIS\n' '(no suitable layers found)')) self.rbHazLayerFromCanvas.setEnabled(False) self.rbHazLayerFromBrowser.click() # Set the memo labels on this and next (hazard) steps (hazard, _, hazard_constraints, _) = self.selected_impact_function_constraints() layer_geometry = hazard_constraints['name'] text = (select_hazard_origin_question % ( layer_geometry, hazard['name'], self.selected_function()['name'])) self.lblSelectHazLayerOriginType.setText(text) text = (select_hazlayer_from_canvas_question % ( layer_geometry, hazard['name'], self.selected_function()['name'])) self.lblSelectHazardLayer.setText(text) text = (select_hazlayer_from_browser_question % ( layer_geometry, hazard['name'], self.selected_function()['name'])) self.lblSelectBrowserHazLayer.setText(text) # =========================== # STEP_FC_HAZLAYER_FROM_CANVAS # ===========================
[docs] 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 = bool( compare_version(kw_ver, get_version()) != 0) else: self.is_selected_layer_keywordless = True desc = self.layer_description_html(layer, keywords) return desc # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_lstCanvasHazLayers_itemSelectionChanged(self): """Update layer description label .. note:: This is an automatic Qt slot executed when the category selection changes. """ self.hazard_layer = self.selected_canvas_hazlayer() lblText = self.get_layer_description_from_canvas(self.hazard_layer, 'hazard') self.lblDescribeCanvasHazLayer.setText(lblText) self.pbnNext.setEnabled(True)
[docs] def selected_canvas_hazlayer(self): """Obtain the canvas layer selected by user. :returns: The currently selected map layer in the list. :rtype: QgsMapLayer """ if self.lstCanvasHazLayers.selectedItems(): item = self.lstCanvasHazLayers.currentItem() else: return None try: layer_id = item.data(QtCore.Qt.UserRole) except (AttributeError, NameError): layer_id = None layer = QgsMapLayerRegistry.instance().mapLayer(layer_id) return layer
[docs] def set_widgets_step_fc_hazlayer_from_canvas(self): """Set widgets on the Hazard Layer From TOC tab""" # The list is already populated in the previous step, but now we # need to do it again in case we're back from the Keyword Wizard. # First, preserve self.layer before clearing the list last_layer = self.layer and self.layer.id() or None self.lblDescribeCanvasHazLayer.clear() self.list_compatible_layers_from_canvas( 'hazard', self.lstCanvasHazLayers) self.auto_select_one_item(self.lstCanvasHazLayers) # Try to select the last_layer, if found: if last_layer: layers = [] for indx in xrange(self.lstCanvasHazLayers.count()): item = self.lstCanvasHazLayers.item(indx) layers += [item.data(QtCore.Qt.UserRole)] if last_layer in layers: self.lstCanvasHazLayers.setCurrentRow(layers.index(last_layer)) # =========================== # STEP_FC_HAZLAYER_FROM_BROWSER # ===========================
[docs] def pg_path_to_uri(self, path): """Convert layer path from QgsBrowserModel to full QgsDataSourceURI :param path: The layer path from QgsBrowserModel :type path: string :returns: layer uri :rtype: QgsDataSourceURI """ conn_name = path.split('/')[1] schema = path.split('/')[2] table = path.split('/')[3] settings = QSettings() key = "/PostgreSQL/connections/" + conn_name service = settings.value(key + "/service") host = settings.value(key + "/host") port = settings.value(key + "/port") if not port: port = "5432" db = settings.value(key + "/database") use_estimated_metadata = settings.value( key + "/estimatedMetadata", False, type=bool) sslmode = settings.value( key + "/sslmode", QgsDataSourceURI.SSLprefer, type=int) username = "" password = "" if settings.value(key + "/saveUsername") == "true": username = settings.value(key + "/username") if settings.value(key + "/savePassword") == "true": password = settings.value(key + "/password") # Old save setting if settings.contains(key + "/save"): username = settings.value(key + "/username") if settings.value(key + "/save") == "true": password = settings.value(key + "/password") uri = QgsDataSourceURI() if service: uri.setConnection(service, db, username, password, sslmode) else: uri.setConnection(host, port, db, username, password, sslmode) uri.setUseEstimatedMetadata(use_estimated_metadata) # Obtain the geometry column name connector = PostGisDBConnector(uri) tbls = connector.getVectorTables(schema) tbls = [tbl for tbl in tbls if tbl[1] == table] # if len(tbls) != 1: # In the future, also look for raster layers? # tbls = connector.getRasterTables(schema) # tbls = [tbl for tbl in tbls if tbl[1]==table] if not tbls: return None tbl = tbls[0] geom_col = tbl[8] uri.setDataSource(schema, table, geom_col) return uri
[docs] def layer_description_html(self, 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 compare_version(keyword_version, get_version()) == 0): # 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>' % ( self.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>' % ( self.tr('Exposure'), keywords.get(purpose)) unit = keywords.get('exposure_unit') else: subcategory = '' unit = None if keywords.get('layer_mode') == layer_mode_classified['key']: unit = self.tr('classified data') if unit: unit = '<tr><td><b>%s</b>: </td><td>%s</td></tr>' % ( self.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> """ % (self.tr('Title'), keywords.get('title'), self.tr('Purpose'), keywords.get('layer_purpose'), subcategory, unit, self.tr('Source'), keywords.get('source')) elif keywords: # The layer has keywords, but the version is wrong desc = self.tr( 'Your layer\'s keyword\'s version (%s) does not match with ' 'your InaSAFE version (%s). 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.' % (keyword_version or 'No 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 = re.sub( r'password=\'.*\'', r'password=*****', layer.source()) desc = """ %s<br/><br/> <b>%s</b>: %s<br/> <b>%s</b>: %s<br/><br/> %s """ % (self.tr('This layer has no valid keywords assigned'), self.tr('SOURCE'), source, self.tr('TYPE'), is_raster_layer(layer) and 'raster' or 'vector (%s)' % geom_type, self.tr('In the next step you will be able' + ' to assign keywords to this layer.')) return desc
[docs] def unsuitable_layer_description_html(self, layer, layer_purpose, keywords=None): """Form a html description of a given non-matching layer based on the currently selected impact function requirements vs layer\'s parameters and keywords if provided, as :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: string :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 """ def emphasize(str1, str2): ''' Compare two strings and emphasize both if differ ''' if str1 != str2: str1 = '<i>%s</i>' % str1 str2 = '<i>%s</i>' % str2 return (str1, str2) # Get allowed subcategory and layer_geometry from IF constraints h, e, hc, ec = self.selected_impact_function_constraints() imfunc = self.selected_function() lay_req = imfunc['layer_requirements'][layer_purpose] if layer_purpose == 'hazard': layer_purpose_key_name = layer_purpose_hazard['name'] req_subcategory = h['key'] req_geometry = hc['key'] elif layer_purpose == 'exposure': layer_purpose_key_name = layer_purpose_exposure['name'] req_subcategory = e['key'] req_geometry = ec['key'] else: layer_purpose_key_name = layer_purpose_aggregation['name'] req_subcategory = '' # For aggregation layers, only accept polygons req_geometry = 'polygon' req_layer_mode = lay_req['layer_mode']['key'] lay_geometry = self.get_layer_geometry_id(layer) lay_purpose = '&nbsp;&nbsp;-' lay_subcategory = '&nbsp;&nbsp;-' lay_layer_mode = '&nbsp;&nbsp;-' if keywords: if 'layer_purpose' in keywords: lay_purpose = keywords['layer_purpose'] if layer_purpose in keywords: lay_subcategory = keywords[layer_purpose] if 'layer_mode' in keywords: lay_layer_mode = keywords['layer_mode'] lay_geometry, req_geometry = emphasize(lay_geometry, req_geometry) lay_purpose, layer_purpose = emphasize(lay_purpose, layer_purpose) lay_subcategory, req_subcategory = emphasize(lay_subcategory, req_subcategory) lay_layer_mode, req_layer_mode = emphasize(lay_layer_mode, req_layer_mode) # Classification classification_row = '' if (lay_req['layer_mode'] == layer_mode_classified and layer_purpose == 'hazard'): # Determine the keyword key for the classification classification_obj = (raster_hazard_classification if is_raster_layer(layer) else vector_hazard_classification) classification_key = classification_obj['key'] classification_key_name = classification_obj['name'] classification_keys = classification_key + 's' if classification_keys in lay_req: allowed_classifications = [ c['key'] for c in lay_req[classification_keys]] req_classifications = ', '.join(allowed_classifications) lay_classification = '&nbsp;&nbsp;-' if classification_key in keywords: lay_classification = keywords[classification_key] if lay_classification not in allowed_classifications: # We already know we want to empasize them and the test # inside the function will always pass. lay_classification, req_classifications = emphasize( lay_classification, req_classifications) classification_row = (('<tr><td><b>%s</b></td>' + '<td>%s</td><td>%s</td></tr>') % (classification_key_name, lay_classification, req_classifications)) # Unit units_row = '' if lay_req['layer_mode'] == layer_mode_continuous: # Determine the keyword key for the unit unit_obj = (continuous_hazard_unit if layer_purpose == layer_purpose_hazard['key'] else exposure_unit) unit_key = unit_obj['key'] unit_key_name = unit_obj['name'] unit_keys = unit_key + 's' if unit_keys in lay_req: allowed_units = [c['key'] for c in lay_req[unit_keys]] req_units = ', '.join(allowed_units) lay_unit = '&nbsp;&nbsp;-' if unit_key in keywords: lay_unit = keywords[unit_key] if lay_unit not in allowed_units: # We already know we want to empasize them and the test # inside the function will always pass. lay_unit, req_units = emphasize(lay_unit, req_units) units_row = (('<tr><td><b>%s</b></td>' + '<td>%s</td><td>%s</td></tr>') % (unit_key_name, lay_unit, req_units)) html = ''' <table border="0" width="100%%" cellpadding="2"> <tr><td width="33%%"></td> <td width="33%%"><b>%s</b></td> <td width="33%%"><b>%s</b></td> </tr> <tr><td><b>%s</b></td><td>%s</td><td>%s</td></tr> <tr><td><b>%s</b></td><td>%s</td><td>%s</td></tr> <tr><td><b>%s</b></td><td>%s</td><td>%s</td></tr> <tr><td><b>%s</b></td><td>%s</td><td>%s</td></tr> %s %s </table> ''' % (self.tr('Layer'), self.tr('Required'), self.tr('Geometry'), lay_geometry, req_geometry, self.tr('Purpose'), lay_purpose, layer_purpose, layer_purpose_key_name, lay_subcategory, req_subcategory, self.tr('Layer mode'), lay_layer_mode, req_layer_mode, classification_row, units_row) return html
[docs] 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', 'QgsLayerItem', 'QgsPGLayerItem']: return False, '' path = item.path() if item_class_name in ['QgsOgrLayerItem', '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): keywords = None # set the layer name for further use in the step_fc_summary if keywords: layer.setLayerName(keywords.get('title')) if not self.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.layer = layer if category == 'hazard': self.hazard_layer = layer elif category == '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 = bool( compare_version(kw_ver, get_version()) != 0) else: self.is_selected_layer_keywordless = True desc = self.layer_description_html(layer, keywords) return True, desc # noinspection PyPep8Naming
[docs] def tvBrowserHazard_selection_changed(self): """Update layer description label""" (is_compatible, desc) = self.get_layer_description_from_browser( 'hazard') self.lblDescribeBrowserHazLayer.setText(desc) self.lblDescribeBrowserHazLayer.setEnabled(is_compatible) self.pbnNext.setEnabled(is_compatible)
[docs] def set_widgets_step_fc_hazlayer_from_browser(self): """Set widgets on the Hazard Layer From Browser tab""" self.tvBrowserHazard_selection_changed() # =========================== # STEP_FC_EXPLAYER_ORIGIN # =========================== # noinspection PyPep8Naming
[docs] def on_rbExpLayerFromCanvas_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbExpLayerFromBrowser_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True)
[docs] def set_widgets_step_fc_explayer_origin(self): """Set widgets on the Exposure 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_explayer_from_canvas because we need # to list them again after coming back from the Keyword Wizard. self.list_compatible_layers_from_canvas( 'exposure', self.lstCanvasExpLayers) if self.lstCanvasExpLayers.count(): self.rbExpLayerFromCanvas.setText(tr( 'I would like to use an exposure layer already loaded in QGIS' '\n' '(launches the %s for exposure if needed)' ) % self.keyword_creation_wizard_name) self.rbExpLayerFromCanvas.setEnabled(True) self.rbExpLayerFromCanvas.click() else: self.rbExpLayerFromCanvas.setText(tr( 'I would like to use an exposure layer already loaded in QGIS' '\n' '(no suitable layers found)')) self.rbExpLayerFromCanvas.setEnabled(False) self.rbExpLayerFromBrowser.click() # Set the memo labels on this and next (exposure) steps (_, exposure, _, exposure_constraints) = self.selected_impact_function_constraints() layer_geometry = exposure_constraints['name'] text = (select_exposure_origin_question % ( layer_geometry, exposure['name'], self.selected_function()['name'])) self.lblSelectExpLayerOriginType.setText(text) text = (select_explayer_from_canvas_question % ( layer_geometry, exposure['name'], self.selected_function()['name'])) self.lblSelectExposureLayer.setText(text) text = (select_explayer_from_browser_question % ( layer_geometry, exposure['name'], self.selected_function()['name'])) self.lblSelectBrowserExpLayer.setText(text) # =========================== # STEP_FC_EXPLAYER_FROM_CANVAS # =========================== # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_lstCanvasExpLayers_itemSelectionChanged(self): """Update layer description label .. note:: This is an automatic Qt slot executed when the category selection changes. """ self.exposure_layer = self.selected_canvas_explayer() lblText = self.get_layer_description_from_canvas(self.exposure_layer, 'exposure') self.lblDescribeCanvasExpLayer.setText(lblText) self.pbnNext.setEnabled(True)
[docs] def selected_canvas_explayer(self): """Obtain the canvas exposure layer selected by user. :returns: The currently selected map layer in the list. :rtype: QgsMapLayer """ if self.lstCanvasExpLayers.selectedItems(): item = self.lstCanvasExpLayers.currentItem() else: return None try: layer_id = item.data(QtCore.Qt.UserRole) except (AttributeError, NameError): layer_id = None layer = QgsMapLayerRegistry.instance().mapLayer(layer_id) return layer
[docs] def set_widgets_step_fc_explayer_from_canvas(self): """Set widgets on the Exposure Layer From Canvas tab""" # The list is already populated in the previous step, but now we # need to do it again in case we're back from the Keyword Wizard. # First, preserve self.layer before clearing the list last_layer = self.layer and self.layer.id() or None self.lblDescribeCanvasExpLayer.clear() self.list_compatible_layers_from_canvas( 'exposure', self.lstCanvasExpLayers) self.auto_select_one_item(self.lstCanvasExpLayers) # Try to select the last_layer, if found: if last_layer: layers = [] for indx in xrange(self.lstCanvasExpLayers.count()): item = self.lstCanvasExpLayers.item(indx) layers += [item.data(QtCore.Qt.UserRole)] if last_layer in layers: self.lstCanvasExpLayers.setCurrentRow(layers.index(last_layer)) # =========================== # STEP_FC_EXPLAYER_FROM_BROWSER # ===========================
[docs] def tvBrowserExposure_selection_changed(self): """Update layer description label""" (is_compatible, desc) = self.get_layer_description_from_browser( 'exposure') self.lblDescribeBrowserExpLayer.setText(desc) self.pbnNext.setEnabled(is_compatible)
[docs] def set_widgets_step_fc_explayer_from_browser(self): """Set widgets on the Exposure Layer From Browser tab""" self.tvBrowserExposure_selection_changed() # =========================== # STEP_FC_DISJOINT_LAYERS # ===========================
[docs] def layers_intersect(self, layer_a, layer_b): """Check if extents of two layers intersect. :param layer_a: One of the two layers to test overlapping :type layer_a: QgsMapLayer :param layer_b: The second of the two layers to test overlapping :type layer_b: QgsMapLayer :returns: true if the layers intersect, false if they are disjoint :rtype: boolean """ extent_a = layer_a.extent() extent_b = layer_b.extent() if layer_a.crs() != layer_b.crs(): coord_transform = QgsCoordinateTransform( layer_a.crs(), layer_b.crs()) extent_b = (coord_transform.transform( extent_b, QgsCoordinateTransform.ReverseTransform)) return extent_a.intersects(extent_b)
[docs] def set_widgets_step_fc_disjoint_layers(self): """Set widgets on the Disjoint Layers tab""" pass # =========================== # STEP_FC_AGGLAYER_ORIGIN # =========================== # noinspection PyPep8Naming
[docs] def on_rbAggLayerFromCanvas_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbAggLayerFromBrowser_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbAggLayerNoAggregation_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True)
[docs] def set_widgets_step_fc_agglayer_origin(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.list_compatible_layers_from_canvas( 'aggregation', self.lstCanvasAggLayers) if self.lstCanvasAggLayers.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.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() # =========================== # STEP_FC_AGGLAYER_FROM_CANVAS # =========================== # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_lstCanvasAggLayers_itemSelectionChanged(self): """Update layer description label .. note:: This is an automatic Qt slot executed when the category selection changes. """ self.aggregation_layer = self.selected_canvas_agglayer() lblText = self.get_layer_description_from_canvas( self.aggregation_layer, 'aggregation') self.lblDescribeCanvasAggLayer.setText(lblText) self.pbnNext.setEnabled(True)
[docs] def selected_canvas_agglayer(self): """Obtain the canvas aggregation layer selected by user. :returns: The currently selected map layer in the list. :rtype: QgsMapLayer """ if self.lstCanvasAggLayers.selectedItems(): item = self.lstCanvasAggLayers.currentItem() else: return None try: layer_id = item.data(QtCore.Qt.UserRole) except (AttributeError, NameError): layer_id = None layer = QgsMapLayerRegistry.instance().mapLayer(layer_id) return layer
[docs] def set_widgets_step_fc_agglayer_from_canvas(self): """Set widgets on the Aggregation Layer from Canvas tab""" # The list is already populated in the previous step, but now we # need to do it again in case we're back from the Keyword Wizard. # First, preserve self.layer before clearing the list last_layer = self.layer and self.layer.id() or None self.lblDescribeCanvasAggLayer.clear() self.list_compatible_layers_from_canvas( 'aggregation', self.lstCanvasAggLayers) self.auto_select_one_item(self.lstCanvasAggLayers) # Try to select the last_layer, if found: if last_layer: layers = [] for indx in xrange(self.lstCanvasAggLayers.count()): item = self.lstCanvasAggLayers.item(indx) layers += [item.data(QtCore.Qt.UserRole)] if last_layer in layers: self.lstCanvasAggLayers.setCurrentRow(layers.index(last_layer)) # =========================== # STEP_FC_AGGLAYER_FROM_BROWSER # =========================== # noinspection PyPep8Naming
[docs] def tvBrowserAggregation_selection_changed(self): """Update layer description label""" (is_compatible, desc) = self.get_layer_description_from_browser( 'aggregation') self.lblDescribeBrowserAggLayer.setText(desc) self.pbnNext.setEnabled(is_compatible)
[docs] def set_widgets_step_fc_agglayer_from_browser(self): """Set widgets on the Aggregation Layer From Browser tab""" self.tvBrowserAggregation_selection_changed() # =========================== # STEP_FC_AGGLAYER_DISJOINT # ===========================
[docs] def set_widgets_step_fc_agglayer_disjoint(self): """Set widgets on the Aggregation Layer Disjoint tab""" pass # =========================== # STEP_FC_EXTENT # =========================== # noinspection PyPep8Naming
[docs] def on_rbExtentUser_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbExtentLayer_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True) # noinspection PyPep8Naming
[docs] def on_rbExtentScreen_toggled(self): """Unlock the Next button .. note:: This is an automatic Qt slot executed when the radiobutton is activated. """ self.pbnNext.setEnabled(True)
[docs] def start_capture_coordinates(self): """Enter the coordinate capture mode""" self.hide()
[docs] def stop_capture_coordinates(self): """Exit the coordinate capture mode""" self.extent_dialog._populate_coordinates() self.extent_dialog.canvas.setMapTool( self.extent_dialog.previous_map_tool) self.show()
[docs] def set_widgets_step_fc_extent(self): """Set widgets on the Extent tab""" # import here only so that it is AFTER i18n set up from safe.gui.tools.extent_selector_dialog import ExtentSelectorDialog self.extent_dialog = ExtentSelectorDialog( self.iface, self.iface.mainWindow(), extent=self.dock.extent.user_extent, crs=self.dock.extent.user_extent_crs) self.extent_dialog.tool.rectangle_created.disconnect( self.extent_dialog.stop_capture) self.extent_dialog.clear_extent.connect( self.dock.extent.clear_user_analysis_extent) self.extent_dialog.extent_defined.connect( self.dock.define_user_analysis_extent) self.extent_dialog.capture_button.clicked.connect( self.start_capture_coordinates) self.extent_dialog.tool.rectangle_created.connect( self.stop_capture_coordinates) self.extent_dialog.label.setText(self.tr( 'Please specify extent of your analysis:')) if self.swExtent: self.swExtent.hide() self.swExtent = self.extent_dialog.main_stacked_widget self.layoutAnalysisExtent.addWidget(self.swExtent)
[docs] def write_extent(self): """ After the extent selection, save the extent and disconnect signals """ self.extent_dialog.accept() self.extent_dialog.clear_extent.disconnect( self.dock.extent.clear_user_analysis_extent) self.extent_dialog.extent_defined.disconnect( self.dock.define_user_analysis_extent) self.extent_dialog.capture_button.clicked.disconnect( self.start_capture_coordinates) self.extent_dialog.tool.rectangle_created.disconnect( self.stop_capture_coordinates) # =========================== # STEP_FC_EXTENT_DISJOINT # ===========================
[docs] def validate_extent(self): """Check if the selected extent intersects source data. :returns: true if extent intersects both layers, false if is disjoint :rtype: boolean """ self.analysis_handler = AnalysisHandler(self) self.analysis_handler.init_analysis() try: self.analysis_handler.analysis.setup_analysis() except InsufficientOverlapError: self.analysis_handler = None return False self.analysis_handler = None return True
[docs] def set_widgets_step_fc_extent_disjoint(self): """Set widgets on the Extent Disjoint tab""" pass # =========================== # STEP_FC_PARAMS # ===========================
[docs] def set_widgets_step_fc_params(self): """Set widgets on the Params tab""" # TODO Put the params to metadata! Now we need to import the IF class. # Notes: Why don't we store impact_function to class attribute? impact_function_id = self.selected_function()['id'] impact_function = self.impact_function_manager.get( impact_function_id) if not impact_function: return self.if_params = None if hasattr(impact_function, 'parameters'): self.if_params = impact_function.parameters text = self.tr( 'Please set impact functions parameters.<br/>Parameters for ' 'impact function "%s" that can be modified are:' % impact_function_id) self.lblSelectIFParameters.setText(text) self.parameter_dialog = FunctionOptionsDialog(self) self.parameter_dialog.set_dialog_info(impact_function_id) self.parameter_dialog.build_form(self.if_params) if self.twParams: self.twParams.hide() self.twParams = self.parameter_dialog.tabWidget self.layoutIFParams.addWidget(self.twParams) # =========================== # STEP_FC_SUMMARY # ===========================
[docs] def set_widgets_step_fc_summary(self): """Set widgets on the Summary tab""" def format_postprocessor(pp): """ make nested OrderedDicts more flat""" if isinstance(pp, OrderedDict): result = [] for v in pp: if isinstance(pp[v], OrderedDict): # omit the v key and unpack the dict directly result += [u'%s: %s' % (unicode(k), unicode(pp[v][k])) for k in pp[v]] else: result += [u'%s: %s' % (unicode(v), unicode(pp[v]))] return u', '.join(result) elif isinstance(pp, list): result = [] for i in pp: name = i.serialize()['name'] val = i.serialize()['value'] if isinstance(val, bool): val = val and self.tr('Enabled') or self.tr('Disabled') if isinstance(i, GroupParameter): # val is a list od *Parameter instances jresult = [] for j in val: jname = j.serialize()['name'] jval = j.serialize()['value'] if isinstance(jval, bool): jval = (jval and self.tr('Enabled') or self.tr('Disabled')) else: jval = unicode(jval) jresult += [u'%s: %s' % (jname, jval)] val = u', '.join(jresult) else: val = unicode(val) if pp.index(i) == 0: result += [val] else: result += [u'%s: %s' % (name, val)] return u', '.join(result) else: return unicode(pp) self.if_params = self.parameter_dialog.parse_input( self.parameter_dialog.values) # (IS) Set the current impact function to use parameter from user. # We should do it prettier (put it on analysis or impact calculator impact_function_id = self.selected_function()['id'] impact_function = self.impact_function_manager.get( impact_function_id) if not impact_function: return impact_function.parameters = self.if_params params = [] for p in self.if_params: if isinstance(self.if_params[p], OrderedDict): subparams = [ u'<tr><td>%s &nbsp;</td><td>%s</td></tr>' % ( unicode(pp), format_postprocessor(self.if_params[p][pp])) for pp in self.if_params[p] ] if subparams: subparams = ''.join(subparams) subparams = '<table border="0">%s</table>' % subparams elif isinstance(self.if_params[p], list) and p == 'minimum needs': subparams = '' for need in self.if_params[p]: # concatenate all ResourceParameter name = unicode(need.serialize()['name']) val = unicode(need.serialize()['value']) if isinstance(need, ResourceParameter): if need.unit and need.unit.abbreviation: val += need.unit.abbreviation subparams += u'<tr><td>%s &nbsp;</td><td>%s</td></tr>' % ( name, val) if subparams: subparams = '<table border="0">%s</table>' % subparams else: subparams = 'Not applicable' elif isinstance(self.if_params[p], list): subparams = ', '.join([unicode(i) for i in self.if_params[p]]) else: subparams = unicode(self.if_params[p].serialize()['value']) params += [(p, subparams)] if self.aggregation_layer: aggr = self.aggregation_layer.name() else: aggr = self.tr('no aggregation') html = self.tr('Please ensure the following information ' 'is correct and press Run.') # TODO: update this to use InaSAFE message API rather... html += '<br/><table cellspacing="4">' html += ('<tr>' ' <td><b>%s</b></td><td width="10"></td><td>%s</td>' '</tr><tr>' ' <td colspan="3"></td>' '</tr><tr>' ' <td><b>%s</b></td><td></td><td>%s</td>' '</tr><tr>' ' <td><b>%s</b></td><td></td><td>%s</td>' '</tr><tr>' ' <td><b>%s</b></td><td></td><td>%s</td>' '</tr><tr>' ' <td colspan="3"></td>' '</tr>' % ( self.tr('impact function').capitalize().replace( ' ', '&nbsp;'), self.selected_function()['name'], self.tr('hazard layer').capitalize().replace( ' ', '&nbsp;'), self.hazard_layer.name(), self.tr('exposure layer').capitalize().replace( ' ', '&nbsp;'), self.exposure_layer.name(), self.tr('aggregation layer').capitalize().replace( ' ', '&nbsp;'), aggr)) def humanize(my_string): """Humanize string. :param my_string: A not human friendly string :type my_string: str :returns: A human friendly string :rtype: str """ my_string = my_string.replace('_', ' ') my_string = my_string.capitalize() return my_string for p in params: html += ( '<tr>' ' <td><b>%s</b></td><td></td><td>%s</td>' '</tr>' % (humanize(p[0]), p[1])) html += '</table>' self.lblSummary.setText(html) # =========================== # STEP_FC_ANALYSIS # =========================== # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_pbnReportWeb_released(self): """Handle the Open Report in Web Browser button release. .. note:: This is an automatic Qt slot executed when the Next button is released. """ self.wvResults.open_current_in_browser() # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_pbnReportPDF_released(self): """Handle the Generate PDF button release. .. note:: This is an automatic Qt slot executed when the Next button is released. """ self.analysis_handler.print_map('pdf') # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_pbnReportComposer_released(self): """Handle the Open Report in Web Broseer button release. .. note:: This is an automatic Qt slot executed when the Next button is released. """ self.analysis_handler.print_map('composer')
[docs] def setup_and_run_analysis(self): """Execute analysis after the tab is displayed""" # noinspection PyTypeChecker self.analysis_handler = AnalysisHandler(self) self.analysis_handler.setup_and_run_analysis()
[docs] def set_widgets_step_fc_analysis(self): """Set widgets on the Progress tab""" self.pbProgress.setValue(0) self.wvResults.setHtml('') self.pbnReportWeb.hide() self.pbnReportPDF.hide() self.pbnReportComposer.hide() self.lblAnalysisStatus.setText(self.tr('Running analysis...')) # =========================== # STEPS NAVIGATION # ===========================
[docs] def go_to_step(self, step): """Set the stacked widget to the given step, set up the buttons, and run all operations that should start immediately after entering the new step. :param step: The step number to be moved to. :type step: int """ self.stackedWidget.setCurrentIndex(step - 1) self.lblStep.clear() # Disable the Next button unless new data already entered self.pbnNext.setEnabled(self.is_ready_to_next_step(step)) # Enable the Back button unless it's not the first step self.pbnBack.setEnabled( step not in [step_kw_category, step_fc_function_1] or self.parent_step is not None) # Set Next button label if (step in [step_kw_title, step_fc_analysis] and self.parent_step is None): self.pbnNext.setText(self.tr('Finish')) elif step == step_fc_summary: self.pbnNext.setText(self.tr('Run')) else: self.pbnNext.setText(self.tr('Next')) # Run analysis after switching to the new step if step == step_fc_analysis: # self.update_MessageViewer_size() self.setup_and_run_analysis() # Set lblSelectCategory label if entering the kw mode # from the ifcw mode if step == step_kw_category and self.parent_step: if self.parent_step in [step_fc_hazlayer_from_canvas, step_fc_hazlayer_from_browser]: text_label = category_question_hazard elif self.parent_step in [step_fc_explayer_from_canvas, step_fc_explayer_from_browser]: text_label = category_question_exposure else: text_label = category_question_aggregation self.lblSelectCategory.setText(text_label) # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_pbnNext_released(self): """Handle the Next button release. .. note:: This is an automatic Qt slot executed when the Next button is released. """ current_step = self.get_current_step() # Save keywords if it's the end of the keyword creation mode if current_step == step_kw_title: self.save_current_keywords() if current_step == step_kw_aggregation: good_age_ratio, sum_age_ratios = self.age_ratios_are_valid() if not good_age_ratio: message = self.tr( 'The sum of age ratio default is %s and it is more ' 'than 1. Please adjust the age ratio default so that they ' 'will not more than 1.' % sum_age_ratios) if not self.suppress_warning_dialog: # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning( self, self.tr('InaSAFE'), message) return # After any step involving Browser, add selected layer to map canvas if current_step in [step_fc_hazlayer_from_browser, step_fc_explayer_from_browser, step_fc_agglayer_from_browser]: if not QgsMapLayerRegistry.instance().mapLayersByName( self.layer.name()): QgsMapLayerRegistry.instance().addMapLayers([self.layer]) # After the extent selection, save the extent and disconnect signals if current_step == step_fc_extent: self.write_extent() # Determine the new step to be switched new_step = self.compute_next_step(current_step) # Prepare the next tab if new_step == step_kw_category: self.set_widgets_step_kw_category() if new_step == step_kw_subcategory: self.set_widgets_step_kw_subcategory() if new_step == step_kw_hazard_category: self.set_widgets_step_kw_hazard_category() elif new_step == step_kw_layermode: self.set_widgets_step_kw_layermode() elif new_step == step_kw_unit: self.set_widgets_step_kw_unit() elif new_step == step_kw_classification: self.set_widgets_step_kw_classification() elif new_step == step_kw_field: self.set_widgets_step_kw_field() elif new_step == step_kw_resample: self.set_widgets_step_kw_resample() elif new_step == step_kw_classify: self.set_widgets_step_kw_classify() elif new_step == step_kw_extrakeywords: self.set_widgets_step_kw_extrakeywords() elif new_step == step_kw_aggregation: self.set_widgets_step_kw_aggregation() elif new_step == step_kw_source: self.set_widgets_step_kw_source() elif new_step == step_kw_title: self.set_widgets_step_kw_title() elif new_step == step_fc_function_1: self.set_widgets_step_fc_function_1() elif new_step == step_fc_function_2: self.set_widgets_step_fc_function_2() elif new_step == step_fc_function_3: self.set_widgets_step_fc_function_3() elif new_step == step_fc_hazlayer_origin: self.set_widgets_step_fc_hazlayer_origin() elif new_step == step_fc_hazlayer_from_canvas: self.set_widgets_step_fc_hazlayer_from_canvas() elif new_step == step_fc_hazlayer_from_browser: self.set_widgets_step_fc_hazlayer_from_browser() elif new_step == step_fc_explayer_origin: self.set_widgets_step_fc_explayer_origin() elif new_step == step_fc_explayer_from_canvas: self.set_widgets_step_fc_explayer_from_canvas() elif new_step == step_fc_explayer_from_browser: self.set_widgets_step_fc_explayer_from_browser() elif new_step == step_fc_disjoint_layers: self.set_widgets_step_fc_disjoint_layers() elif new_step == step_fc_agglayer_origin: self.set_widgets_step_fc_agglayer_origin() elif new_step == step_fc_agglayer_from_canvas: self.set_widgets_step_fc_agglayer_from_canvas() elif new_step == step_fc_agglayer_from_browser: self.set_widgets_step_fc_agglayer_from_browser() elif new_step == step_fc_agglayer_disjoint: self.set_widgets_step_fc_agglayer_disjoint() elif new_step == step_fc_extent: self.set_widgets_step_fc_extent() elif new_step == step_fc_extent_disjoint: self.set_widgets_step_fc_extent_disjoint() elif new_step == step_fc_params: self.set_widgets_step_fc_params() elif new_step == step_fc_summary: self.set_widgets_step_fc_summary() elif new_step == step_fc_analysis: self.set_widgets_step_fc_analysis() elif new_step is None: # Wizard complete self.accept() return else: # unknown step pass self.go_to_step(new_step) # prevents actions being handled twice # noinspection PyPep8Naming
@pyqtSignature('')
[docs] def on_pbnBack_released(self): """Handle the Back button release. .. note:: This is an automatic Qt slot executed when the Back button is released. """ current_step = self.get_current_step() new_step = self.compute_previous_step(current_step) # set focus to table widgets, as the inactive selection style is gray if new_step == step_fc_function_1: self.tblFunctions1.setFocus() if new_step == step_fc_function_2: self.tblFunctions2.setFocus() # Re-connect disconnected signals when coming back to the Extent step if new_step == step_fc_extent: self.set_widgets_step_fc_extent() # Set Next button label self.pbnNext.setText(self.tr('Next')) self.pbnNext.setEnabled(True) self.go_to_step(new_step)
[docs] def is_ready_to_next_step(self, step): """Check if the step we enter is initially complete. If so, there is no reason to block the Next button. :param step: The present step number. :type step: int :returns: True if new step may be enabled. :rtype: bool """ if step == step_kw_category: return bool(self.selected_category()) if step == step_kw_subcategory: return bool(self.selected_subcategory()) if step == step_kw_hazard_category: return bool(self.selected_hazard_category()) if step == step_kw_layermode: return bool(self.selected_layermode()) if step == step_kw_unit: return bool(self.selected_unit()) if step == step_kw_classification: return bool(self.selected_classification()) if step == step_kw_field: return bool(self.selected_field() or not self.lstFields.count()) if step == step_kw_resample: return True if step == step_kw_classify: # Allow to not classify any values return True if step == step_kw_extrakeywords: return self.are_all_extra_keywords_selected() if step == step_kw_aggregation: # Not required return True if step == step_kw_source: # The source_* keywords are not required return True if step == step_kw_title: return bool(self.leTitle.text()) if step == step_fc_function_1: return bool(self.tblFunctions1.selectedItems()) if step == step_fc_function_2: return bool(self.tblFunctions2.selectedItems()) if step == step_fc_function_3: return bool(self.selected_function()) if step == step_fc_hazlayer_origin: return (bool(self.rbHazLayerFromCanvas.isChecked() or self.rbHazLayerFromBrowser.isChecked())) if step == step_fc_hazlayer_from_canvas: return bool(self.selected_canvas_hazlayer()) if step == step_fc_hazlayer_from_browser: return self.get_layer_description_from_browser('hazard')[0] if step == step_fc_explayer_origin: return (bool(self.rbExpLayerFromCanvas.isChecked() or self.rbExpLayerFromBrowser.isChecked())) if step == step_fc_explayer_from_canvas: return bool(self.selected_canvas_explayer()) if step == step_fc_explayer_from_browser: return self.get_layer_description_from_browser('exposure')[0] if step == step_fc_disjoint_layers: # Never go further if layers disjoint return False if step == step_fc_agglayer_origin: return (bool(self.rbAggLayerFromCanvas.isChecked() or self.rbAggLayerFromBrowser.isChecked() or self.rbAggLayerNoAggregation.isChecked())) if step == step_fc_agglayer_from_canvas: return bool(self.selected_canvas_agglayer()) if step == step_fc_agglayer_from_browser: return self.get_layer_description_from_browser('aggregation')[0] if step == step_fc_agglayer_disjoint: # Never go further if layers disjoint return False if step == step_fc_extent: return True if step == step_fc_params: return True if step == step_fc_summary: return True if step == step_fc_analysis: return True return True
[docs] def compute_next_step(self, current_step): """Determine the next step to be switched to. :param current_step: The present step number. :type current_step: int :returns: The next step number or None if finished. :rtype: int """ if current_step == step_kw_category: if self.selected_category() == layer_purpose_aggregation: new_step = step_kw_field else: new_step = step_kw_subcategory elif current_step == step_kw_subcategory: if self.selected_category() == layer_purpose_hazard: new_step = step_kw_hazard_category else: new_step = step_kw_layermode elif current_step == step_kw_hazard_category: new_step = step_kw_layermode elif current_step == step_kw_layermode: if self.selected_layermode() == layer_mode_classified: if is_point_layer(self.layer) \ and self.selected_category() == layer_purpose_hazard: # Skip FIELD and CLASSIFICATION for point volcanos new_step = step_kw_extrakeywords elif self.classifications_for_layer(): new_step = step_kw_classification elif is_raster_layer(self.layer): new_step = step_kw_extrakeywords else: new_step = step_kw_field else: # CONTINUOUS DATA, ALL GEOMETRIES new_step = step_kw_unit elif current_step == step_kw_unit: if is_raster_layer(self.layer): if self.selected_category() == layer_purpose_exposure: # Only go to resample for continuous raster exposures new_step = step_kw_resample else: new_step = step_kw_extrakeywords else: # Currently not used, as we don't have continuous vectors new_step = step_kw_field elif current_step == step_kw_classification: if is_raster_layer(self.layer): new_step = step_kw_classify else: new_step = step_kw_field elif current_step == step_kw_field: if self.selected_category() == layer_purpose_aggregation: new_step = step_kw_aggregation elif self.selected_layermode() == layer_mode_classified and \ self.classifications_for_layer(): new_step = step_kw_classify else: new_step = step_kw_extrakeywords elif current_step == step_kw_resample: new_step = step_kw_extrakeywords elif current_step == step_kw_classify: new_step = step_kw_extrakeywords elif current_step == step_kw_extrakeywords: new_step = step_kw_source elif current_step == step_kw_aggregation: new_step = step_kw_source elif current_step == step_kw_source: new_step = step_kw_title elif current_step == step_kw_title: if self.parent_step: # Come back to the parent thread new_step = self.parent_step self.parent_step = None self.is_selected_layer_keywordless = False self.set_mode_label_to_ifcw() else: # Wizard complete new_step = None elif current_step == step_fc_hazlayer_origin: if self.rbHazLayerFromCanvas.isChecked(): new_step = step_fc_hazlayer_from_canvas else: new_step = step_fc_hazlayer_from_browser elif current_step in [step_fc_hazlayer_from_canvas, step_fc_hazlayer_from_browser]: if self.is_selected_layer_keywordless: # insert keyword creation thread here self.parent_step = current_step self.existing_keywords = None self.set_mode_label_to_keywords_creation() new_step = step_kw_category else: new_step = step_fc_explayer_origin elif current_step == step_fc_explayer_origin: if self.rbExpLayerFromCanvas.isChecked(): new_step = step_fc_explayer_from_canvas else: new_step = step_fc_explayer_from_browser elif current_step in [step_fc_explayer_from_canvas, step_fc_explayer_from_browser]: if self.is_selected_layer_keywordless: # insert keyword creation thread here self.parent_step = current_step self.existing_keywords = None self.set_mode_label_to_keywords_creation() new_step = step_kw_category else: if not self.layers_intersect(self.hazard_layer, self.exposure_layer): new_step = step_fc_disjoint_layers else: new_step = step_fc_agglayer_origin elif current_step == step_fc_disjoint_layers: new_step = step_fc_agglayer_origin elif current_step == step_fc_agglayer_origin: if self.rbAggLayerFromCanvas.isChecked(): new_step = step_fc_agglayer_from_canvas elif self.rbAggLayerFromBrowser.isChecked(): new_step = step_fc_agglayer_from_browser else: new_step = step_fc_extent elif current_step in [step_fc_agglayer_from_canvas, step_fc_agglayer_from_browser]: if self.is_selected_layer_keywordless: # insert keyword creation thread here self.parent_step = current_step self.existing_keywords = None self.set_mode_label_to_keywords_creation() new_step = step_kw_category else: flag = self.layers_intersect( self.exposure_layer, self.aggregation_layer) if not flag: new_step = step_fc_agglayer_disjoint else: new_step = step_fc_extent elif current_step == step_fc_agglayer_disjoint: new_step = step_fc_extent elif current_step == step_fc_extent: if self.validate_extent(): new_step = step_fc_params else: new_step = step_fc_extent_disjoint elif current_step in [step_fc_function_1, step_fc_function_2, step_fc_function_3, step_fc_params, step_fc_summary]: new_step = current_step + 1 elif current_step == step_fc_analysis: new_step = None # Wizard complete elif current_step < self.stackedWidget.count(): raise Exception('Unhandled step') else: raise Exception('Unexpected number of steps') # Skip the extra_keywords tab if no extra keywords available: if (new_step == step_kw_extrakeywords and not self.additional_keywords_for_the_layer()): new_step = step_kw_source return new_step
[docs] def compute_previous_step(self, current_step): """Determine the previous step to be switched to (by the Back button). :param current_step: The present step number. :type current_step: int :returns: The previous step number. :rtype: int """ if current_step == step_kw_category: if self.parent_step: # Come back to the parent thread self.set_mode_label_to_ifcw() new_step = self.parent_step self.parent_step = None else: new_step = step_kw_category elif current_step == step_kw_subcategory: new_step = step_kw_category elif current_step == step_kw_hazard_category: new_step = step_kw_subcategory elif current_step == step_kw_layermode: if self.selected_category() == layer_purpose_hazard: new_step = step_kw_hazard_category else: new_step = step_kw_subcategory elif current_step == step_kw_unit: new_step = step_kw_layermode elif current_step == step_kw_classification: new_step = step_kw_layermode elif current_step == step_kw_field: if self.selected_category() == layer_purpose_aggregation: new_step = step_kw_category elif self.selected_layermode() == layer_mode_continuous: new_step = step_kw_unit elif self.classifications_for_layer(): new_step = step_kw_classification else: new_step = step_kw_layermode elif current_step == step_kw_resample: new_step = step_kw_unit elif current_step == step_kw_classify: if is_raster_layer(self.layer): new_step = step_kw_classification else: new_step = step_kw_field elif current_step == step_kw_aggregation: new_step = step_kw_field elif current_step == step_kw_extrakeywords: if self.selected_layermode() == layer_mode_classified: if self.selected_classification(): new_step = step_kw_classify elif self.selected_field(): new_step = step_kw_field else: new_step = step_kw_layermode else: if self.selected_allowresampling() is not None: new_step = step_kw_resample else: new_step = step_kw_unit elif current_step == step_kw_source: if self.selected_category() == layer_purpose_aggregation: new_step = step_kw_aggregation elif self.selected_extra_keywords(): new_step = step_kw_extrakeywords # otherwise behave like it was step_kw_extrakeywords elif self.selected_layermode() == layer_mode_classified: if self.selected_classification(): new_step = step_kw_classify elif self.selected_field(): new_step = step_kw_field else: new_step = step_kw_layermode else: if self.selected_allowresampling() is not None: new_step = step_kw_resample else: new_step = step_kw_unit elif current_step == step_kw_title: new_step = step_kw_source elif current_step == step_fc_function_1: new_step = step_fc_function_1 elif current_step == step_fc_hazlayer_from_browser: new_step = step_fc_hazlayer_origin elif current_step == step_fc_explayer_origin: if self.rbHazLayerFromCanvas.isChecked(): new_step = step_fc_hazlayer_from_canvas else: new_step = step_fc_hazlayer_from_browser elif current_step == step_fc_explayer_from_browser: new_step = step_fc_explayer_origin elif current_step == step_fc_disjoint_layers: if self.rbExpLayerFromCanvas.isChecked(): new_step = step_fc_explayer_from_canvas else: new_step = step_fc_explayer_from_browser elif current_step == step_fc_agglayer_origin: if self.rbExpLayerFromCanvas.isChecked(): new_step = step_fc_explayer_from_canvas else: new_step = step_fc_explayer_from_browser elif current_step == step_fc_agglayer_from_browser: new_step = step_fc_agglayer_origin elif current_step == step_fc_agglayer_disjoint: if self.rbAggLayerFromCanvas.isChecked(): new_step = step_fc_agglayer_from_canvas else: new_step = step_fc_agglayer_from_browser elif current_step == step_fc_extent: if self.rbAggLayerFromCanvas.isChecked(): new_step = step_fc_agglayer_from_canvas elif self.rbAggLayerFromBrowser.isChecked(): new_step = step_fc_agglayer_from_browser else: new_step = step_fc_agglayer_origin elif current_step == step_fc_params: new_step = step_fc_extent else: new_step = current_step - 1 return new_step # =========================== # COMMON METHODS # ===========================
[docs] def get_current_step(self): """Return current step of the wizard. :returns: Current step of the wizard. :rtype: int """ return self.stackedWidget.currentIndex() + 1
[docs] def get_layer_geometry_id(self, layer=None): """Obtain layer mode of a given layer. If no layer specified, the current layer is used :param layer : layer to examine :type layer: QgsMapLayer or None :returns: The layer mode. :rtype: str """ if not layer: layer = self.layer if is_raster_layer(layer): return 'raster' elif is_point_layer(layer): return 'point' elif is_polygon_layer(layer): return 'polygon' else: return 'line'
[docs] def get_existing_keyword(self, keyword): """Obtain an existing keyword's value. :param keyword: A keyword from keywords. :type keyword: str :returns: The value of the keyword. :rtype: str """ if self.existing_keywords is None: return None if keyword is not None: return self.existing_keywords.get(keyword, None) else: return None
[docs] def get_keywords(self): """Obtain the state of the dialog as a keywords dict. :returns: Keywords reflecting the state of the dialog. :rtype: dict """ keywords = {} keywords['layer_geometry'] = self.get_layer_geometry_id() if self.selected_category(): keywords['layer_purpose'] = self.selected_category()['key'] if keywords['layer_purpose'] == 'aggregation': keywords.update(self.get_aggregation_attributes()) if self.selected_subcategory(): key = self.selected_category()['key'] keywords[key] = self.selected_subcategory()['key'] if self.selected_hazard_category(): keywords['hazard_category'] \ = self.selected_hazard_category()['key'] if self.selected_layermode(): keywords['layer_mode'] = self.selected_layermode()['key'] if self.selected_unit(): if self.selected_category() == layer_purpose_hazard: key = continuous_hazard_unit['key'] else: key = exposure_unit['key'] keywords[key] = self.selected_unit()['key'] if self.selected_allowresampling() is not None: keywords['allow_resampling'] = ( self.selected_allowresampling() and 'true' or 'false') if self.lstFields.currentItem(): field_keyword = self.field_keyword_for_the_layer() keywords[field_keyword] = self.lstFields.currentItem().text() if self.selected_classification(): geom = 'raster' if is_raster_layer(self.layer) else 'vector' key = '%s_%s_classification' % (geom, self.selected_category()['key']) keywords[key] = self.selected_classification()['key'] value_map = self.selected_mapping() if value_map: keywords['value_map'] = json.dumps(value_map) extra_keywords = self.selected_extra_keywords() for key in extra_keywords: keywords[key] = extra_keywords[key] if self.leSource.text(): keywords['source'] = get_unicode(self.leSource.text()) if self.leSource_url.text(): keywords['url'] = get_unicode(self.leSource_url.text()) if self.leSource_scale.text(): keywords['scale'] = get_unicode(self.leSource_scale.text()) if self.leSource_date.text(): keywords['date'] = get_unicode(self.leSource_date.text()) if self.leSource_license.text(): keywords['license'] = get_unicode(self.leSource_license.text()) if self.leTitle.text(): keywords['title'] = get_unicode(self.leTitle.text()) return keywords
[docs] 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, e: error_message = get_error_message(e) # noinspection PyCallByClass,PyTypeChecker,PyArgumentList QtGui.QMessageBox.warning( self, self.tr('InaSAFE'), ((self.tr( 'An error was encountered when saving the following ' 'keywords:\n %s') % error_message.to_html()))) if self.dock is not None: # noinspection PyUnresolvedReferences self.dock.get_layers() # noinspection PyUnresolvedReferences,PyMethodMayBeStatic
[docs] def auto_select_one_item(self, list_widget): """Select item in the list in list_widget if it's the only item. :param list_widget: The list widget that want to be checked. :type list_widget: QListWidget """ if list_widget.count() == 1 and list_widget.currentRow() == -1: list_widget.setCurrentRow(0)
[docs] def set_tool_tip(self): """Set tool tip as helper text for some objects.""" title_tooltip = self.tr('Title of the layer.') source_tooltip = self.tr( 'Please record who is the custodian of this layer i.e. ' 'OpenStreetMap') date_tooltip = self.tr( 'When was this data collected or downloaded i.e. 1-May-2014') scale_tooltip = self.tr('What is the scale of this layer?') url_tooltip = self.tr( 'Does the custodians have their own website ' 'i.e. www.openstreetmap.org') self.lblTitle.setToolTip(title_tooltip) self.lblSource.setToolTip(source_tooltip) self.lblDate.setToolTip(date_tooltip) self.lblScale.setToolTip(scale_tooltip) self.lblURL.setToolTip(url_tooltip) self.leTitle.setToolTip(title_tooltip) self.leSource.setToolTip(source_tooltip) self.leSource_date.setToolTip(date_tooltip) self.leSource_scale.setToolTip(scale_tooltip) self.leSource_url.setToolTip(url_tooltip)