def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ layer_map = QgsMapLayerRegistry.instance().mapLayers() if not bool(layer_map): QMessageBox.warning( self, "STDM", "You must add a layer first from Spatial Unit " "Manager to import GPX to") elif bool(layer_map): self.gps_tool_dialog = GPSToolDialog(self.iface, self.curr_layer, self.curr_lyr_table, self.curr_lyr_sp_col) self.gps_tool_dialog.show()
def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ source_status = self.active_layer_source() layer_map = QgsMapLayerRegistry.instance().mapLayers() error_title = QApplication.translate( 'SpatialUnitManagerDockWidget', 'GPS Feature Import Loading Error' ) if len(layer_map) > 0: if source_status is None: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have not selected a layer.\n ' 'Please select a valid layer to import GPS features.' ) ) elif source_status is False: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have selected a non-STDM entity layer.\n ' 'Please select a valid layer to import GPS features.' ) ) elif source_status is True: self.gps_tool_dialog = GPSToolDialog( self.iface, self.active_entity, self.active_table, self.active_sp_col ) self.gps_tool_dialog.show() else: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You must add an entity layer from Spatial Unit Manager\n' 'and select it to import GPS Features.' ) )
def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ layer_map = QgsMapLayerRegistry.instance().mapLayers() if not bool(layer_map): QMessageBox.warning(None,"STDM","You must add a layer first, from Spatial Unit Manager to import GPX to") elif bool(layer_map): self.gps_tool_dialog = GPSToolDialog(self.iface, self.curr_layer, self.curr_lyr_table, self.curr_lyr_sp_col) self.gps_tool_dialog.show()
class SpatialUnitManagerDockWidget(QDockWidget, Ui_SpatialUnitManagerWidget): onLayerAdded = pyqtSignal(str, object) def __init__(self, iface, plugin=None): """Constructor.""" QDockWidget.__init__(self, iface.mainWindow()) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface self._plugin = plugin self.gps_tool_dialog = None # properties of last added layer self.curr_lyr_table = None self.curr_lyr_sp_col = None # properties of the active layer self.active_entity = None self.active_table = None self.active_sp_col = None self.style_updated = None self.setMaximumHeight(300) self._curr_profile = current_profile() self._profile_spatial_layers = [] self.stdm_fields = STDMFieldWidget() self._populate_layers() self._adjust_layer_drop_down_width() self.spatial_unit = None self.iface.currentLayerChanged.connect(self.control_digitize_toolbar) self.onLayerAdded.connect(self.init_spatial_form) self.add_to_canvas_button.clicked.connect( self.on_add_to_canvas_button_clicked) self.iface.projectRead.connect(self.on_project_opened) def on_project_opened(self): legend_layers = self.iface.legendInterface().layers() for layer in legend_layers: source = self.layer_source(layer) if source is not bool and source is not None: self.init_spatial_form(self.active_sp_col, layer) def get_column_config(self, config, name): """ Gets joined column name config. :param config: The config object :type config: ColumnConfig :param name: The column name :type name:String :return: :rtype: """ configs = [c for c in config.columns() if c.name == name] if len(configs) > 0: return configs[0] else: return None def sort_joined_columns(self, layer, fk_fields): """ Sort joined columns using the order in the configuration :param layer: The layer containing joined layers :type layer: QgsVectorLayer :return: :rtype: """ if not hasattr(layer, 'attributeTableConfig'): return entity = self._curr_profile.entity_by_name(self.curr_lyr_table) config = layer.attributeTableConfig() columns = config.columns() updated_columns = [] for column in columns: if column.name not in entity.columns.keys(): continue if column.name in fk_fields.keys(): # hide the lookup id column column.hidden = True header = entity.columns[column.name].header() joined_column_name = u'{} {}'.format(header, fk_fields[column.name]) joined_column = self.get_column_config(config, joined_column_name) if joined_column is not None: updated_columns.append(joined_column) updated_columns.append(column) else: updated_columns.append(column) config.setColumns(updated_columns) layer.setAttributeTableConfig(config) @staticmethod def execute_layers_join(layer, layer_field, column_header, fk_layer, fk_field): """ Joins two layers with specified field. :param layer: The destination layer of the merge. :type layer: QgsVectorLayer :param layer_field: The source layer of the merge. :type layer_field: String :param fk_layer: The foreign key layer object. :type fk_layer: QgsVectorLayer :param fk_field: The foreign key layer field name. :type fk_field: String :return: :rtype: """ join = QgsVectorJoinInfo() join.joinLayerId = fk_layer.id() join.joinFieldName = 'id' join.setJoinFieldNamesSubset([fk_field]) join.targetFieldName = layer_field join.memoryCache = True join.prefix = u'{} '.format(column_header) layer.addJoin(join) def column_to_fk_layer_join(self, column, layer, join_field): """ Creates and executes the join by creating fk layer and running the join method using a column object. :param column: The column object :type column: Object :param layer: The layer to contain the joined fk layer :type layer: QgsVectorLayer :param join_field: The join field that is in the fk layer :type join_field: String """ fk_entity = column.entity_relation.parent fk_layer = vector_layer(fk_entity.name, layer_name=fk_entity.name) QgsMapLayerRegistry.instance().addMapLayer(fk_layer, False) # hide the fk id column column.hidden = True header = column.header() self.execute_layers_join(layer, column.name, header, fk_layer, join_field) def join_fk_layer(self, layer, entity): """ Joins foreign key to the layer by creating and choosing join fields. :param layer: The layer to contain the joined fk layer :type layer: QgsVectorLayer :param entity: The layer entity object :type entity: Object :return: A dictionary containing fk_field and column object :rtype: OrderedDict """ if entity is None: return fk_columns = OrderedDict() for column in entity.columns.values(): if column.TYPE_INFO == 'LOOKUP': fk_column = 'value' elif column.TYPE_INFO == 'ADMIN_SPATIAL_UNIT': fk_column = 'name' elif column.TYPE_INFO == 'FOREIGN_KEY': display_cols = column.entity_relation.display_cols if len(display_cols) > 0: fk_column = display_cols[0] else: fk_column = 'id' else: fk_column = None if fk_column is not None: self.column_to_fk_layer_join(column, layer, fk_column) fk_columns[column.name] = fk_column return fk_columns def _adjust_layer_drop_down_width(self): """ Adjusts the layers combobox drop down to expand based on the layer name. """ if len(self._profile_spatial_layers) > 0: longest_item = max(self._profile_spatial_layers, key=len) font_meter = QFontMetrics(self.fontMetrics()) item_width = font_meter.width(longest_item) + 80 self.stdm_layers_combo.setStyleSheet('''* QComboBox QAbstractItemView{ min-width: 60px; width: %s px; } ''' % item_width) def _populate_layers(self): self.stdm_layers_combo.clear() if self._curr_profile is None: return self.spatial_units = self._curr_profile.social_tenure.spatial_units # Get entities containing geometry # columns based on the config info config_entities = self._curr_profile.entities self.geom_entities = [ ge for ge in config_entities.values() if ge.TYPE_INFO == 'ENTITY' and ge.has_geometry_column() ] self._profile_spatial_layers = [] self.sp_tables = spatial_tables() for e in self.geom_entities: table_name = e.name if table_name in self.sp_tables: for i, gc in enumerate(e.geometry_columns()): column_name = gc.name display_name = gc.layer_display() if i > 0: display_name = u'{}.{}'.format(display_name, gc.name) self._add_geometry_column_to_combo(table_name, column_name, display_name, gc) # Add geometry entity to the collection self._profile_spatial_layers.append(table_name) # Append the corresponding(profile) view to the list of entity names str_views = self._curr_profile.social_tenure.views.keys() for str_view in str_views: if str_view in self.sp_tables: self.str_view_geom_columns = table_column_names(str_view, True) if len(self.str_view_geom_columns) > 0: # Pick the first column for i, geom_col in enumerate(self.str_view_geom_columns): view_layer_name = str_view if i > 0: view_layer_name = '{}.{}'.format( view_layer_name, geom_col) self._add_geometry_column_to_combo( str_view, geom_col, view_layer_name, self._curr_profile.social_tenure) # Append view to the list of spatial layers self._profile_spatial_layers.append(str_view) # add old config views and custom views. for sp_table in self.sp_tables: if sp_table in pg_views() and sp_table not in str_views and \ sp_table in profile_and_user_views( self._curr_profile): view_geom_columns = table_column_names(sp_table, True) for geom_col in view_geom_columns: view_layer_name = '{}.{}'.format(sp_table, geom_col) self._add_geometry_column_to_combo(sp_table, geom_col, view_layer_name, geom_col) def control_digitize_toolbar(self, curr_layer): if not curr_layer is None: try: table, column = self._layer_table_column(curr_layer) if table not in pg_views(): # Make sure digitizing toolbar is enabled self.iface.digitizeToolBar().setEnabled(True) self.set_canvas_crs(curr_layer) elif table in pg_views(): self.iface.digitizeToolBar().setEnabled(False) except Exception: pass def set_canvas_crs(self, layer): # Sets canvas CRS # get srid with EPSG text if layer.isValid(): full_srid = layer.crs().authid().split(':') srid = int(full_srid[1]) layer_crs = QgsCoordinateReferenceSystem( srid, QgsCoordinateReferenceSystem.EpsgCrsId) self.iface.mapCanvas().mapSettings().setDestinationCrs(layer_crs) def init_spatial_form(self, spatial_column, curr_layer): """ Initializes the Layer form. :param curr_layer: The layer for which the widgets are set. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneType """ table, column = self._layer_table_column(curr_layer) if table not in pg_views() and not curr_layer is None: try: self.stdm_fields.init_form(table, spatial_column, curr_layer) except Exception as ex: LOGGER.debug(unicode(ex)) def _format_layer_display_name(self, col, table): return u'{0}.{1}'.format(table, col) def _add_geometry_column_to_combo(self, table_name, column_name, display, item): icon = self._geom_icon(table_name, column_name) self.stdm_layers_combo.addItem(icon, display, { 'table_name': table_name, 'column_name': column_name, 'item': item }) for spatial_unit in self.spatial_units: table = spatial_unit.name spatial_column = [ c.name for c in spatial_unit.columns.values() if c.TYPE_INFO == 'GEOMETRY' ] spatial_unit_item = unicode(table + '.' + spatial_column[0]) index = self.stdm_layers_combo.findText(spatial_unit_item, Qt.MatchFixedString) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) def _layer_info_from_table_column(self, table, column): # Returns the index and item # data from the given table and column name idx, layer_info = -1, None for i in range(self.stdm_layers_combo.count()): layer_info = self.stdm_layers_combo.itemData(i) if layer_info['table_name'] == table and \ layer_info['column_name'] == column: idx, layer_info = i, layer_info break return idx, layer_info def _geom_icon(self, table, column): # Get column type and apply the appropriate icon geometry_typ = unicode(geometryType(table, column)[0]) icon = None if geometry_typ == 'POLYGON': icon = QIcon(':/plugins/stdm/images/icons/layer_polygon.png') elif geometry_typ == 'LINESTRING': icon = QIcon(':/plugins/stdm/images/icons/layer_line.png') elif geometry_typ == 'POINT': icon = QIcon(':/plugins/stdm/images/icons/layer_point.png') elif geometry_typ == 'MULTIPOLYGON': icon = QIcon(':/plugins/stdm/images/icons/layer_polygon.png') elif geometry_typ == 'MULTILINESTRING': icon = QIcon(':/plugins/stdm/images/icons/layer_line.png') else: icon = QIcon(':/plugins/stdm/images/icons/table.png') return icon def add_layer_by_name(self, layer_name): """ Add a layer when a name is supplied. :param layer_name: The stdm layer name :type layer_name: String """ index = self.stdm_layers_combo.findText(layer_name, Qt.MatchFixedString) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) # add spatial unit layer. self.on_add_to_canvas_button_clicked() def geom_col_layer_name(self, table, col): """ Returns the layer name based on geom column object. :param col: Column Object :type col: Object :param table: Table name :type table: String :return: Layer name :rtype: String """ # Check if the geom has display name, if not, # get layer name with default naming. if col is None: return if isinstance(col, str) or isinstance(col, unicode): spatial_layer_item = u'{}.{}'.format(table, col) elif isinstance(col, SocialTenure): spatial_layer_item = col.view_name elif col.layer_display_name == '': spatial_layer_item = u'{0}'.format(col.entity.short_name) # use the layer_display_name else: spatial_layer_item = col.layer_display_name return spatial_layer_item def on_add_to_canvas_button_clicked(self): """ Add STDM layer to map canvas. """ if self.stdm_layers_combo.count() == 0: return sp_col_info = self.stdm_layers_combo.itemData( self.stdm_layers_combo.currentIndex()) if sp_col_info is None: title = QApplication.translate('SpatialUnitManagerDockWidget', 'Spatial Unit Manager') msg = QApplication.translate( 'SpatialUnitManagerDockWidget', 'Spatial Column Layer Could not be found') # Message: Spatial column information # could not be found QMessageBox.warning(self.iface.mainWindow(), title, msg) table_name, spatial_column = sp_col_info["table_name"], \ sp_col_info["column_name"] # Check if the layer has already been layer_item = sp_col_info.get('item', None) layer_name = self.geom_col_layer_name(table_name, layer_item) if layer_name in self._map_registry_layer_names(): layer = QgsMapLayerRegistry.instance().mapLayersByName( layer_name)[0] self.iface.setActiveLayer(layer) return self.curr_lyr_table = table_name self.curr_lyr_sp_col = spatial_column if not layer_item is None: if isinstance(layer_item, str) or isinstance(layer_item, unicode): layer_name = layer_item else: layer_name = layer_item.layer_display() entity = self._curr_profile.entity_by_name(table_name) if entity is not None: geom_col_obj = entity.columns[spatial_column] srid = None if geom_col_obj.srid >= 100000: srid = geom_col_obj.srid curr_layer = vector_layer(table_name, geom_column=spatial_column, layer_name=layer_name, proj_wkt=srid) else: curr_layer = vector_layer(table_name, geom_column=spatial_column, layer_name=layer_name, proj_wkt=None) # for lookup layer. else: curr_layer = vector_layer(table_name, geom_column=spatial_column) if curr_layer.isValid(): if curr_layer.name() in self._map_registry_layer_names(): return QgsMapLayerRegistry.instance().addMapLayer(curr_layer) self.zoom_to_layer() self.onLayerAdded.emit(spatial_column, curr_layer) self.toggle_entity_multi_layers(curr_layer) self.set_canvas_crs(curr_layer) # Required in order for the layer name to be set if layer_name is not None: QTimer.singleShot( 100, lambda: self._set_layer_display_name( curr_layer, layer_name)) entity = self._curr_profile.entity_by_name(self.curr_lyr_table) fk_fields = self.join_fk_layer(curr_layer, entity) if entity is not None: self.sort_joined_columns(curr_layer, fk_fields) self.set_field_alias(curr_layer, entity, fk_fields) else: msg = QApplication.translate( "Spatial Unit Manager", "'{0}.{1}' layer is invalid, it cannot " "be added to the map view.".format(table_name, spatial_column)) QMessageBox.critical(self.iface.mainWindow(), 'Spatial Unit Manager', msg) def set_field_alias(self, layer, entity, fk_fields): """ Set the field alia for fk joined fields so that they are same as the child columns. :param layer: The layer containing the join :type layer: QgsVectorLayer :param entity: The entity of the layer :type entity: Object :param fk_fields: The dictionary containing the parent and child fields :type fk_fields: OrderedDict :return: :rtype: """ for column, fk_field in fk_fields.iteritems(): header = entity.columns[column].header() f_index = layer.fieldNameIndex(u'{} {}'.format(header, fk_field)) alias = u'{} Value'.format(header) layer.addAttributeAlias(f_index, alias) def zoom_to_layer(self): """ Zooms the map canvas to the extent the active layer. :return: :rtype: """ layer = self.iface.activeLayer() if not layer is None: self.iface.mapCanvas().setExtent(layer.extent()) self.iface.mapCanvas().refresh() def geom_columns(self, entity): """ Returns the geometry columns of an entity. :param entity: The entity object. :type entity: Object :return: List of Geometry column objects :rtype: List """ geom_column = [ column for column in entity.columns.values() if column.TYPE_INFO == 'GEOMETRY' ] return geom_column def same_entity_layers(self): """ Returns layer names of an entity if they are more than one in one entity. :return: List in a list :rtype: List """ entity_layers = [] for entity in self.geom_entities: layer_list = self.entity_layer_names(entity) entity_layers.append(layer_list) return entity_layers def entity_layer_names(self, entity): """ Returns layer names of an entity if they are more than one in one entity. :param entity: The Entity object :param type: Object :return: List in a list :rtype: List """ cols = self.geom_columns(entity) layer_list = [] for col in cols: lyr_name = self.geom_col_layer_name(entity.name, col) layer_list.append(lyr_name) return layer_list def layer_entity_children(self, sel_lyr_name): layer_lists = [ layer_list for layer_list in self.same_entity_layers() if sel_lyr_name in layer_list ] str_view = self._curr_profile.social_tenure.view_name if len(layer_lists) < 1: geom_columns = table_column_names(str_view, True) layer_lists = [geom_columns] return layer_lists def toggle_entity_multi_layers(self, new_layer): """ Removes other layers created from the entity of the new layer. :param new_layer: The new layer added :type new_layer: QgsVectorLayer :return: None :rtype: NoneType """ sel_lyr_name = new_layer.name() layer_lists = self.layer_entity_children(sel_lyr_name) # Include layers for toggling whose entity # has more than one geometry if len(layer_lists) < 1: return # if the layer_list is the # parent of selected layer for layer_name in layer_lists[0]: # remove other layers # of the same entity if layer_name != sel_lyr_name: layer_objects = QgsMapLayerRegistry. \ instance().mapLayersByName(layer_name) if len(layer_objects) > 0: for layer in layer_objects: layer_id = layer.id() QgsMapLayerRegistry. \ instance().removeMapLayer(layer_id) # Change the crs of the canvas based on the new layer layer_list = QgsMapLayerRegistry.instance(). \ mapLayersByName(sel_lyr_name) if len(layer_list) > 0: self.set_canvas_crs(layer_list[0]) def _set_layer_display_name(self, layer, name): try: layer.setName(name) except RuntimeError: pass def _layer_table_column(self, layer): # Returns the table and column name # that a layer belongs to. table, column = '', '' if hasattr(layer, 'dataProvider'): if layer.dataProvider().name() == 'postgres': layerConnStr = layer.dataProvider().dataSourceUri() dataSourceURI = QgsDataSourceURI(layerConnStr) table, column = dataSourceURI.table(), \ dataSourceURI.geometryColumn() return table, column def _map_registry_layer_names(self): """ Returns a list of layers names. """ layers = QgsMapLayerRegistry.instance().mapLayers() layer_names = [lyr.name() for lyr in layers.values()] return layer_names @pyqtSignature("") def on_set_display_name_button_clicked(self): """ Method to change display name """ layer_names_ids = pg_layerNamesIDMapping().reverse layer = self.iface.activeLayer() if not layer is None: table_name = layer_names_ids.get(layer.id(), '') if table_name: # Check if the table name is in the current profile if table_name in self._profile_spatial_layers: prompt = \ u"Set the display name for '{0}' layer".format( layer.name() ) display_name, ok = QInputDialog.getText( self, 'Spatial Unit ' 'Manager', prompt) if ok and display_name: # Get layer table and columns names table, column = self._layer_table_column(layer) if table and column: idx, layer_info = \ self._layer_info_from_table_column( table, column ) # Get item in the combo corresponding to the layer if idx != -1: self.stdm_layers_combo.setItemText( idx, display_name) layer.setName(display_name) # Update configuration item config_item = layer_info.get('item', None) if not config_item is None: config_item.layer_display_name = \ display_name # Update configuration save_configuration() else: msg = QApplication.translate( "Spatial Unit Manager", u"The layer does not " u"belong in the '{0}' " u"profile.\nThe display name " u"will not be set." u"".format(self._curr_profile.name)) QMessageBox.critical(self.iface.mainWindow(), 'Spatial Unit Manager', msg) def active_layer_source(self): """ Get the layer table name if the source is from the database. :return: The a Boolean True if a valid source is found or False if not. Alternatively, None if there is no active layer. :rtype: Boolean or NoneType """ active_layer = self.iface.activeLayer() return self.layer_source(active_layer) def layer_source(self, layer): """ Gets the layer source. :param layer: The layer :type layer: Any :return: Layer Source or None :rtype: """ if layer is None: return None source = layer.source() if source is None: return False source_value = dict(re.findall('(\S+)="?(.*?)"? ', source)) try: table = source_value['table'].split('.') table_name = table[1].strip('"') if table_name in pg_views(): return False entity = self._curr_profile.entity_by_name(table_name) if entity is None: return False else: self.active_entity = entity self.active_table = table_name # get all spatial columns of the entity. spatial_columns = [ c.name for c in entity.columns.values() if c.TYPE_INFO == 'GEOMETRY' ] # get all fields excluding the geometry. layer_fields = [ field.name() for field in layer.pendingFields() ] # get the currently being used geometry column active_sp_cols = [ col for col in spatial_columns if col not in layer_fields ] if len(active_sp_cols) == 1: self.active_sp_col = active_sp_cols[0] return True except KeyError: return False @pyqtSignature("") def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ source_status = self.active_layer_source() layer_map = QgsMapLayerRegistry.instance().mapLayers() error_title = QApplication.translate( 'SpatialUnitManagerDockWidget', 'GPS Feature Import Loading Error') if len(layer_map) > 0: if source_status is None: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have not selected a layer.\n ' 'Please select a valid layer to import GPS features.')) elif source_status is False: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have selected a non-STDM entity layer.\n ' 'Please select a valid layer to import GPS features.')) elif source_status is True: self.gps_tool_dialog = GPSToolDialog(self.iface, self.active_entity, self.active_table, self.active_sp_col) self.gps_tool_dialog.show() else: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You must add an entity layer from Spatial Unit Manager\n' 'and select it to import GPS Features.')) def _valid_entity(self): """ Checks if the current active layer in the layer panel represents a valid entity in the current profile :return: Error object :rtype: Object """ entity_profile = current_profile() entity_obj = entity_profile.entity_by_name(self.curr_lyr_table) if entity_obj is None: return None return entity_obj def closeEvent(self, event): """ On close of the dock window, this event is executed to run close_dock method :param event: The close event :type QCloseEvent :return: None """ self._plugin.spatialLayerManager.setChecked(False)
class SpatialUnitManagerDockWidget(QDockWidget, Ui_SpatialUnitManagerWidget): def __init__(self, iface): """Constructor.""" QDockWidget.__init__(self, iface.mainWindow()) # Set up the user interface from Designer. # After setupUI you can access any designer object by doing # self.<objectname>, and you can use autoconnect slots - see # http://qt-project.org/doc/qt-4.8/designer-using-a-ui-file.html # #widgets-and-dialogs-with-auto-connect self.setupUi(self) self._populate_layers() self.iface = iface self.gps_tool_dialog = None self.curr_lyr_table = None self.curr_lyr_sp_col = None self.curr_layer = None def _populate_layers(self): self.stdm_layers_combo.clear() self._stdm_tables = {} self.spatial_layers = [] self.layers_info = [] for spt in spatial_tables(): sp_columns = table_column_names(spt,True) self._stdm_tables[spt]=sp_columns # QMessageBox.information(None,"Title",str(sp_columns)) # Add spatial columns to combo box for sp_col in sp_columns: # Get column type and apply the appropriate icon geometry_typ = str(geometryType(spt, sp_col)[0]) if geometry_typ == "POLYGON": self.icon = QIcon(":/plugins/stdm/images/icons/layer_polygon.png") elif geometry_typ == "LINESTRING": self.icon = QIcon(":/plugins/stdm/images/icons/layer_line.png") elif geometry_typ == "POINT": self.icon = QIcon(":/plugins/stdm/images/icons/layer_point.png") else: self.icon = QIcon(":/plugins/stdm/images/icons/table.png") # QMessageBox.information(None,"Title",str(geometry_typ)) self.stdm_layers_combo.addItem(self.icon, self._format_layer_display_name(sp_col, spt), {"table_name":spt,"col_name": sp_col}) def _format_layer_display_name(self, col, table): return u"{0}.{1}".format(table,col) @pyqtSignature("") def on_add_to_canvas_button_clicked(self): ''' Method used to add layers to canvas ''' if self.stdm_layers_combo.count() == 0: # Return message that there are no layers QMessageBox.warning(None,"No Layers") sp_col_info = self.stdm_layers_combo.itemData(self.stdm_layers_combo.currentIndex()) if sp_col_info is None: # Message: Spatial column information could not be found QMessageBox.warning(None,"Spatial Column Layer Could not be found") table_name, spatial_column = sp_col_info["table_name"], sp_col_info["col_name"] # Used in gpx_table.py self.curr_lyr_table = table_name self.curr_lyr_sp_col = spatial_column self.curr_layer = vector_layer(table_name, geom_column=spatial_column) if self.curr_layer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(self.curr_layer) # Append column name to spatial table _current_display_name = str(table_name) + "." + str(spatial_column) # Get configuration file display name if it exists if check_if_display_name_exits(_current_display_name): xml_display_name = get_xml_display_name(_current_display_name) self.curr_layer.setLayerName(xml_display_name) # Write initial display name as original name of the layer elif not check_if_display_name_exits(_current_display_name): write_display_name(_current_display_name, _current_display_name) self.curr_layer.setLayerName(_current_display_name) @pyqtSignature("") def on_set_display_name_button_clicked(self): ''' Method to change display name ''' layer_map = QgsMapLayerRegistry.instance().mapLayers() for name, layer in layer_map.iteritems(): if layer == self.iface.activeLayer(): _name = layer.name() display_name, ok = QInputDialog.getText(None,"Change Display Name", "Current Name is {0}".format(layer.originalName())) if ok and display_name != "": layer.setLayerName(display_name) write_changed_display_name(_name, display_name) elif not ok and display_name == "": layer.originalName() else: continue @pyqtSignature("") def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ layer_map = QgsMapLayerRegistry.instance().mapLayers() if not bool(layer_map): QMessageBox.warning(None,"STDM","You must add a layer first, from Spatial Unit Manager to import GPX to") elif bool(layer_map): self.gps_tool_dialog = GPSToolDialog(self.iface, self.curr_layer, self.curr_lyr_table, self.curr_lyr_sp_col) self.gps_tool_dialog.show()
class SpatialUnitManagerDockWidget(QDockWidget, Ui_SpatialUnitManagerWidget): onLayerAdded = pyqtSignal(str, object) def __init__(self, iface, plugin=None): """Constructor.""" QDockWidget.__init__(self, iface.mainWindow()) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface self._plugin = plugin self.gps_tool_dialog = None self.curr_lyr_table = None self.curr_lyr_sp_col = None self.curr_layer = None self.setMaximumHeight(300) self._curr_profile = current_profile() self._profile_spatial_layers = [] self.stdm_fields = STDMFieldWidget() self._populate_layers() self.feature_details = DetailsTreeView(iface, self) self.spatial_unit = None self.iface.currentLayerChanged.connect(self.control_digitize_toolbar) self.onLayerAdded.connect(self.init_spatial_form) self.add_to_canvas_button.clicked.connect( self.on_add_to_canvas_button_clicked) self.feature_details_btn.clicked.connect( self.feature_details.activate_feature_details) self.iface.currentLayerChanged.connect( lambda: self.feature_details.activate_feature_details(False)) def _populate_layers(self): self.stdm_layers_combo.clear() if self._curr_profile is None: return self.spatial_unit = self._curr_profile. \ social_tenure.spatial_unit # Get entities containing geometry # columns based on the config info config_entities = self._curr_profile.entities self.geom_entities = [ ge for ge in config_entities.values() if ge.TYPE_INFO == 'ENTITY' and ge.has_geometry_column() ] self._profile_spatial_layers = [] self.sp_tables = spatial_tables() for e in self.geom_entities: table_name = e.name if table_name in self.sp_tables: for gc in e.geometry_columns(): column_name = gc.name display_name = gc.layer_display() self._add_geometry_column_to_combo(table_name, column_name, display_name, gc) # Add geometry entity to the collection self._profile_spatial_layers.append(table_name) # Append the corresponding(profile) # view to the list of entity names str_view = self._curr_profile.social_tenure.view_name if str_view in self.sp_tables: self.str_view_geom_columns = table_column_names(str_view, True) if len(self.str_view_geom_columns) > 0: # Pick the first column # geom_col = geom_columns[0] for i, geom_col in enumerate(self.str_view_geom_columns): if i > 0: view_layer_name = self._curr_profile. \ social_tenure.layer_display() view_layer_name = '{}.{}'.format( view_layer_name, geom_col) else: view_layer_name = self._curr_profile. \ social_tenure.layer_display() self._add_geometry_column_to_combo( str_view, geom_col, view_layer_name, self._curr_profile.social_tenure) # Append view to the list of spatial layers self._profile_spatial_layers.append(view_layer_name) def control_digitize_toolbar(self, curr_layer): if not curr_layer is None: try: table, column = self._layer_table_column(curr_layer) if table not in pg_views(): # Make sure digitizing toolbar is enabled self.iface.digitizeToolBar().setEnabled(True) self.set_canvas_crs(curr_layer) elif table in pg_views(): self.iface.digitizeToolBar().setEnabled(False) except Exception: pass def set_canvas_crs(self, layer): # Sets canvas CRS # get srid with EPSG text if layer.isValid(): full_srid = layer.crs().authid().split(':') srid = int(full_srid[1]) layer_crs = QgsCoordinateReferenceSystem( srid, QgsCoordinateReferenceSystem.EpsgCrsId) self.iface.mapCanvas().mapRenderer().setDestinationCrs(layer_crs) def init_spatial_form(self, spatial_column, curr_layer): """ Initializes the Layer form. :param curr_layer: The layer for which the widgets are set. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneType """ table, column = self._layer_table_column(curr_layer) if table not in pg_views() and not curr_layer is None: try: self.stdm_fields.init_form(table, spatial_column, curr_layer) except Exception as ex: LOGGER.debug(unicode(ex)) def _format_layer_display_name(self, col, table): return u'{0}.{1}'.format(table, col) def _add_geometry_column_to_combo(self, table_name, column_name, display, item): icon = self._geom_icon(table_name, column_name) self.stdm_layers_combo.addItem(icon, display, { 'table_name': table_name, 'column_name': column_name, 'item': item }) table = self.spatial_unit.name spatial_column = [ c.name for c in self.spatial_unit.columns.values() if c.TYPE_INFO == 'GEOMETRY' ] spatial_unit_item = unicode(table + '.' + spatial_column[0]) index = self.stdm_layers_combo.findText(spatial_unit_item, Qt.MatchFixedString) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) def _layer_info_from_table_column(self, table, column): # Returns the index and item # data from the given table and column name idx, layer_info = -1, None for i in range(self.stdm_layers_combo.count()): layer_info = self.stdm_layers_combo.itemData(i) if layer_info['table_name'] == table and \ layer_info['column_name'] == column: idx, layer_info = i, layer_info break return idx, layer_info def _geom_icon(self, table, column): # Get column type and apply the appropriate icon geometry_typ = unicode(geometryType(table, column)[0]) icon = None if geometry_typ == 'POLYGON': icon = QIcon(':/plugins/stdm/images/icons/layer_polygon.png') elif geometry_typ == 'LINESTRING': icon = QIcon(':/plugins/stdm/images/icons/layer_line.png') elif geometry_typ == 'POINT': icon = QIcon(':/plugins/stdm/images/icons/layer_point.png') else: icon = QIcon(':/plugins/stdm/images/icons/table.png') return icon def add_layer_by_name(self, layer_name): """ Add a layer when a name is supplied. :param layer_name: The stdm layer name :type layer_name: String :return: None :rtype: NoneType """ index = self.stdm_layers_combo.findText(layer_name, Qt.MatchFixedString) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) # add spatial unit layer. self.on_add_to_canvas_button_clicked() def geom_col_layer_name(self, table, col): """ Returns the layer name based on geom column object. :param col: Column Object :type col: Object :param table: Table name :type table: String :return: Layer name :rtype: String """ # Check if the geom has display name, if not, # get layer name with default naming. if col.layer_display_name == '': spatial_layer_item = unicode('{}.{}'.format(table, col.name)) # use the layer_display_name else: spatial_layer_item = col.layer_display_name return spatial_layer_item def on_add_to_canvas_button_clicked(self): """ Add STDM layer to map canvas. """ if self.stdm_layers_combo.count() == 0: return sp_col_info = self.stdm_layers_combo.itemData( self.stdm_layers_combo.currentIndex()) if sp_col_info is None: title = QApplication.translate('SpatialUnitManagerDockWidget', 'Spatial Unit Manager') msg = QApplication.translate( 'SpatialUnitManagerDockWidget', 'Spatial Column Layer Could not be found') # Message: Spatial column information # could not be found QMessageBox.warning(self.iface.mainWindow(), title, msg) table_name, spatial_column = sp_col_info["table_name"], \ sp_col_info["column_name"] # Check if the layer has already been layer_item = sp_col_info.get('item', None) layer_name = self.geom_col_layer_name(table_name, layer_item) if layer_name in self._map_registry_layer_names(): return # Used in gpx_table.py self.curr_lyr_table = table_name self.curr_lyr_sp_col = spatial_column if not layer_item is None: curr_layer = vector_layer(table_name, geom_column=spatial_column, layer_name=layer_item.layer_display()) else: curr_layer = vector_layer(table_name, geom_column=spatial_column) if curr_layer.isValid(): QgsMapLayerRegistry.instance().addMapLayer(curr_layer) self.toggle_entity_multi_layers(curr_layer) self.set_canvas_crs(curr_layer) # Required in order for the layer name to be set QTimer.singleShot( 100, lambda: self._set_layer_display_name( curr_layer, layer_item.layer_display())) self.zoom_to_layer() self.onLayerAdded.emit(spatial_column, curr_layer) else: msg = QApplication.translate( "Spatial Unit Manager", "'{0}.{1}' layer is invalid, it cannot " "be added to the map view.".format(table_name, spatial_column)) QMessageBox.critical(self.iface.mainWindow(), 'Spatial Unit Manager', msg) def zoom_to_layer(self): """ Zooms the map canvas to the extent the active layer. :return: :rtype: """ layer = self.iface.activeLayer() if not layer is None: self.iface.mapCanvas().setExtent(layer.extent()) self.iface.mapCanvas().refresh() def geom_columns(self, entity): """ Get the geometry column :return: :rtype: """ geom_column = [ column for column in entity.columns.values() if column.TYPE_INFO == 'GEOMETRY' ] return geom_column def same_entity_layers(self): """ Returns layer names of an entity if they are more than one in one entity. :return: List in a list :rtype: List """ entity_layers = [] for entity in self.geom_entities: layer_list = self.entity_layer_names(entity) entity_layers.append(layer_list) return entity_layers def entity_layer_names(self, entity): """ Returns layer names of an entity if they are more than one in one entity. :param entity: The Entity object :param type: Object :return: List in a list :rtype: List """ cols = self.geom_columns(entity) layer_list = [] for col in cols: lyr_name = self.geom_col_layer_name(entity.name, col) layer_list.append(lyr_name) return layer_list def layer_entity_children(self, sel_lyr_name): layer_lists = [ layer_list for layer_list in self.same_entity_layers() if sel_lyr_name in layer_list ] str_view = self._curr_profile.social_tenure.view_name if len(layer_lists) < 1: geom_columns = table_column_names(str_view, True) layer_lists = [geom_columns] return layer_lists def toggle_entity_multi_layers(self, new_layer): """ Removes other layers created from the entity of the new layer. :param new_layer: The new layer added :type new_layer: QgsVectorLayer :return: None :rtype: NoneType """ sel_lyr_name = new_layer.name() layer_lists = self.layer_entity_children(sel_lyr_name) # Include layers for toggling whose entity # has more than one geometry if len(layer_lists) < 1: return # if the layer_list is the # parent of selected layer for layer_name in layer_lists[0]: # remove other layers # of the same entity if layer_name != sel_lyr_name: layer_objects = QgsMapLayerRegistry. \ instance().mapLayersByName(layer_name) if len(layer_objects) > 0: for layer in layer_objects: layer_id = layer.id() QgsMapLayerRegistry. \ instance().removeMapLayer(layer_id) # Change the crs of the canvas based on the new layer layer_list = QgsMapLayerRegistry.instance(). \ mapLayersByName(sel_lyr_name) if len(layer_list) > 0: self.set_canvas_crs(layer_list[0]) def _set_layer_display_name(self, layer, name): try: layer.setLayerName(name) except RuntimeError: pass def _layer_table_column(self, layer): # Returns the table and column name # that a layer belongs to. table, column = '', '' if hasattr(layer, 'dataProvider'): if layer.dataProvider().name() == 'postgres': layerConnStr = layer.dataProvider().dataSourceUri() dataSourceURI = QgsDataSourceURI(layerConnStr) table, column = dataSourceURI.table(), \ dataSourceURI.geometryColumn() return table, column def _map_registry_layer_names(self): """ Returns a list of layers names. """ layers = QgsMapLayerRegistry.instance().mapLayers() layer_names = [lyr.name() for lyr in layers.values()] return layer_names @pyqtSignature("") def on_set_display_name_button_clicked(self): """ Method to change display name """ layer_names_ids = pg_layerNamesIDMapping().reverse layer = self.iface.activeLayer() if not layer is None: table_name = layer_names_ids.get(layer.id(), '') if table_name: # Check if the table name is in the current profile if table_name in self._profile_spatial_layers: prompt = \ u"Set the display name for '{0}' layer".format( layer.name() ) display_name, ok = QInputDialog.getText( self, 'Spatial Unit ' 'Manager', prompt) if ok and display_name: # Get layer table and columns names table, column = self._layer_table_column(layer) if table and column: idx, layer_info = \ self._layer_info_from_table_column( table, column ) # Get item in the combo corresponding to the layer if idx != -1: self.stdm_layers_combo.setItemText( idx, display_name) layer.setLayerName(display_name) # Update configuration item config_item = layer_info.get('item', None) if not config_item is None: config_item.layer_display_name = \ display_name # Update configuration save_configuration() else: msg = QApplication.translate( "Spatial Unit Manager", u"The layer does not " u"belong in the '{0}' " u"profile.\nThe display name " u"will not be set." u"".format(self._curr_profile.name)) QMessageBox.critical(self.iface.mainWindow(), 'Spatial Unit Manager', msg) @pyqtSignature("") def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ layer_map = QgsMapLayerRegistry.instance().mapLayers() if not bool(layer_map): QMessageBox.warning( self, "STDM", "You must add a layer first from Spatial Unit " "Manager to import GPX to") elif bool(layer_map): self.gps_tool_dialog = GPSToolDialog(self.iface, self.curr_layer, self.curr_lyr_table, self.curr_lyr_sp_col) self.gps_tool_dialog.show() def closeEvent(self, event): """ On close of the dock window, this event is executed to run close_dock method :param event: The close event :type QCloseEvent :return: None """ self._plugin.spatialLayerManager.setChecked(False)
class SpatialUnitManagerDockWidget( QDockWidget, Ui_SpatialUnitManagerWidget ): onLayerAdded = pyqtSignal(str, object) def __init__(self, iface, plugin=None): """Constructor.""" QDockWidget.__init__(self, iface.mainWindow()) # Set up the user interface from Designer. self.setupUi(self) self.iface = iface self._plugin = plugin self.gps_tool_dialog = None # properties of last added layer self.curr_lyr_table = None self.curr_lyr_sp_col = None # properties of the active layer self.active_entity = None self.active_table = None self.active_sp_col = None self.style_updated = None self.setMaximumHeight(300) self._curr_profile = current_profile() self._profile_spatial_layers = [] self.stdm_fields = STDMFieldWidget(plugin) self._populate_layers() self._adjust_layer_drop_down_width() self.spatial_unit = None self.iface.currentLayerChanged.connect( self.control_digitize_toolbar ) self.onLayerAdded.connect( self.init_spatial_form ) self.add_to_canvas_button.clicked.connect( self.on_add_to_canvas_button_clicked ) self.iface.projectRead.connect(self.on_project_opened) def on_project_opened(self): legend_layers = self.iface.legendInterface().layers() for layer in legend_layers: source = self.layer_source(layer) if source is not bool and source is not None: self.init_spatial_form(self.active_sp_col, layer) def get_column_config(self, config, name): """ Gets joined column name config. :param config: The config object :type config: ColumnConfig :param name: The column name :type name:String :return: :rtype: """ configs = [c for c in config.columns() if c.name == name] if len(configs) > 0: return configs[0] else: return None def sort_joined_columns(self, layer, fk_fields): """ Sort joined columns using the order in the configuration :param layer: The layer containing joined layers :type layer: QgsVectorLayer :return: :rtype: """ if not hasattr(layer, 'attributeTableConfig'): return entity = self._curr_profile.entity_by_name(self.curr_lyr_table) config = layer.attributeTableConfig() columns = config.columns() updated_columns = [] for column in columns: if column.name not in entity.columns.keys(): continue if column.name in fk_fields.keys(): # hide the lookup id column column.hidden = True header = entity.columns[column.name].header() joined_column_name = u'{} {}'.format(header, fk_fields[column.name]) joined_column = self.get_column_config(config, joined_column_name) if joined_column is not None: updated_columns.append(joined_column) updated_columns.append(column) else: updated_columns.append(column) config.setColumns(updated_columns) layer.setAttributeTableConfig(config) @staticmethod def execute_layers_join(layer, layer_field, column_header, fk_layer, fk_field): """ Joins two layers with specified field. :param layer: The destination layer of the merge. :type layer: QgsVectorLayer :param layer_field: The source layer of the merge. :type layer_field: String :param fk_layer: The foreign key layer object. :type fk_layer: QgsVectorLayer :param fk_field: The foreign key layer field name. :type fk_field: String :return: :rtype: """ join = QgsVectorJoinInfo() join.joinLayerId = fk_layer.id() join.joinFieldName = 'id' join.setJoinFieldNamesSubset([fk_field]) join.targetFieldName = layer_field join.memoryCache = True join.prefix = u'{} '.format(column_header) layer.addJoin(join) def column_to_fk_layer_join(self, column, layer, join_field): """ Creates and executes the join by creating fk layer and running the join method using a column object. :param column: The column object :type column: Object :param layer: The layer to contain the joined fk layer :type layer: QgsVectorLayer :param join_field: The join field that is in the fk layer :type join_field: String """ fk_entity = column.entity_relation.parent fk_layer = vector_layer( fk_entity.name, layer_name=fk_entity.name ) QgsMapLayerRegistry.instance().addMapLayer( fk_layer, False ) # hide the fk id column column.hidden = True header = column.header() self.execute_layers_join(layer, column.name, header, fk_layer, join_field) def join_fk_layer(self, layer, entity): """ Joins foreign key to the layer by creating and choosing join fields. :param layer: The layer to contain the joined fk layer :type layer: QgsVectorLayer :param entity: The layer entity object :type entity: Object :return: A dictionary containing fk_field and column object :rtype: OrderedDict """ if entity is None: return fk_columns = OrderedDict() for column in entity.columns.values(): if column.TYPE_INFO == 'LOOKUP': fk_column = 'value' elif column.TYPE_INFO == 'ADMIN_SPATIAL_UNIT': fk_column = 'name' elif column.TYPE_INFO == 'FOREIGN_KEY': display_cols = column.entity_relation.display_cols if len(display_cols) > 0: fk_column = display_cols[0] else: fk_column = 'id' else: fk_column = None if fk_column is not None: self.column_to_fk_layer_join(column, layer, fk_column) fk_columns[column.name] = fk_column return fk_columns def _adjust_layer_drop_down_width(self): """ Adjusts the layers combobox drop down to expand based on the layer name. """ if len(self._profile_spatial_layers) > 0: longest_item = max(self._profile_spatial_layers, key=len) font_meter = QFontMetrics(self.fontMetrics()) item_width = font_meter.width(longest_item) + 80 self.stdm_layers_combo.setStyleSheet( '''* QComboBox QAbstractItemView{ min-width: 60px; width: %s px; } ''' % item_width ) def _populate_layers(self): self.stdm_layers_combo.clear() if self._curr_profile is None: return self.spatial_units = self._curr_profile.social_tenure.spatial_units # Get entities containing geometry # columns based on the config info config_entities = self._curr_profile.entities self.geom_entities = [ ge for ge in config_entities.values() if ge.TYPE_INFO == 'ENTITY' and ge.has_geometry_column() ] self._profile_spatial_layers = [] self.sp_tables = spatial_tables() for e in self.geom_entities: table_name = e.name if table_name in self.sp_tables: for i, gc in enumerate(e.geometry_columns()): column_name = gc.name display_name = gc.layer_display() if i > 0: display_name = u'{}.{}'.format(display_name, gc.name) self._add_geometry_column_to_combo( table_name, column_name, display_name, gc ) # Add geometry entity to the collection self._profile_spatial_layers.append( table_name ) # Append the corresponding(profile) view to the list of entity names str_views = self._curr_profile.social_tenure.views.keys() for str_view in str_views: if str_view in self.sp_tables: self.str_view_geom_columns = table_column_names( str_view, True ) if len(self.str_view_geom_columns) > 0: # Pick the first column for i, geom_col in enumerate(self.str_view_geom_columns): view_layer_name = str_view if i > 0: view_layer_name = '{}.{}'.format( view_layer_name, geom_col ) self._add_geometry_column_to_combo( str_view, geom_col, view_layer_name, self._curr_profile.social_tenure ) # Append view to the list of spatial layers self._profile_spatial_layers.append( str_view ) # add old config views and custom views. for sp_table in self.sp_tables: if sp_table in pg_views() and sp_table not in str_views and \ sp_table in profile_and_user_views( self._curr_profile): view_geom_columns = table_column_names( sp_table, True ) for geom_col in view_geom_columns: view_layer_name = '{}.{}'.format( sp_table, geom_col ) self._add_geometry_column_to_combo( sp_table, geom_col, view_layer_name, geom_col ) def control_digitize_toolbar(self, curr_layer): if not curr_layer is None: try: table, column = self._layer_table_column( curr_layer ) if table not in pg_views(): # Make sure digitizing toolbar is enabled self.iface.digitizeToolBar().setEnabled(True) self.set_canvas_crs(curr_layer) elif table in pg_views(): self.iface.digitizeToolBar().setEnabled(False) except Exception: pass def set_canvas_crs(self, layer): # Sets canvas CRS # get srid with EPSG text if layer.isValid(): full_srid = layer.crs().authid().split(':') srid = int(full_srid[1]) layer_crs = QgsCoordinateReferenceSystem( srid, QgsCoordinateReferenceSystem.EpsgCrsId ) self.iface.mapCanvas().mapSettings().setDestinationCrs(layer_crs) def init_spatial_form(self, spatial_column, curr_layer): """ Initializes the Layer form. :param curr_layer: The layer for which the widgets are set. :type curr_layer: QgsVectorLayer :return: None :rtype: NoneType """ table, column = self._layer_table_column(curr_layer) if table not in pg_views() and not curr_layer is None: try: self.stdm_fields.init_form( table, spatial_column, curr_layer ) except Exception as ex: LOGGER.debug(unicode(ex)) def _format_layer_display_name(self, col, table): return u'{0}.{1}'.format(table, col) def _add_geometry_column_to_combo( self, table_name, column_name, display, item ): icon = self._geom_icon(table_name, column_name) self.stdm_layers_combo.addItem(icon, display, { 'table_name': table_name, 'column_name': column_name, 'item': item } ) for spatial_unit in self.spatial_units: table = spatial_unit.name spatial_column = [ c.name for c in spatial_unit.columns.values() if c.TYPE_INFO == 'GEOMETRY' ] spatial_unit_item = unicode( table + '.' + spatial_column[0] ) index = self.stdm_layers_combo.findText( spatial_unit_item, Qt.MatchFixedString ) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) def _layer_info_from_table_column( self, table, column ): # Returns the index and item # data from the given table and column name idx, layer_info = -1, None for i in range(self.stdm_layers_combo.count()): layer_info = self.stdm_layers_combo.itemData(i) if layer_info['table_name'] == table and \ layer_info['column_name'] == column: idx, layer_info = i, layer_info break return idx, layer_info def _geom_icon(self, table, column): # Get column type and apply the appropriate icon geometry_typ = unicode(geometryType(table, column)[0]) icon = None if geometry_typ == 'POLYGON': icon = QIcon( ':/plugins/stdm/images/icons/layer_polygon.png' ) elif geometry_typ == 'LINESTRING': icon = QIcon( ':/plugins/stdm/images/icons/layer_line.png' ) elif geometry_typ == 'POINT': icon = QIcon( ':/plugins/stdm/images/icons/layer_point.png' ) elif geometry_typ == 'MULTIPOLYGON': icon = QIcon( ':/plugins/stdm/images/icons/layer_polygon.png' ) elif geometry_typ == 'MULTILINESTRING': icon = QIcon( ':/plugins/stdm/images/icons/layer_line.png' ) else: icon = QIcon( ':/plugins/stdm/images/icons/table.png' ) return icon def add_layer_by_name(self, layer_name): """ Add a layer when a name is supplied. :param layer_name: The stdm layer name :type layer_name: String """ index = self.stdm_layers_combo.findText( layer_name, Qt.MatchFixedString ) if index >= 0: self.stdm_layers_combo.setCurrentIndex(index) # add spatial unit layer. self.on_add_to_canvas_button_clicked() def geom_col_layer_name(self, table, col): """ Returns the layer name based on geom column object. :param col: Column Object :type col: Object :param table: Table name :type table: String :return: Layer name :rtype: String """ # Check if the geom has display name, if not, # get layer name with default naming. if col is None: return if isinstance(col, str) or isinstance(col, unicode): spatial_layer_item = u'{}.{}'.format(table, col) elif isinstance(col, SocialTenure): spatial_layer_item = col.view_name elif col.layer_display_name == '': spatial_layer_item = u'{0}'.format(col.entity.short_name) # use the layer_display_name else: spatial_layer_item = col.layer_display_name return spatial_layer_item def on_add_to_canvas_button_clicked(self): """ Add STDM layer to map canvas. """ if self.stdm_layers_combo.count() == 0: return sp_col_info = self.stdm_layers_combo.itemData( self.stdm_layers_combo.currentIndex() ) if sp_col_info is None: title = QApplication.translate( 'SpatialUnitManagerDockWidget', 'Spatial Unit Manager' ) msg = QApplication.translate( 'SpatialUnitManagerDockWidget', 'Spatial Column Layer Could not be found' ) # Message: Spatial column information # could not be found QMessageBox.warning(self.iface.mainWindow(), title, msg) table_name, spatial_column = sp_col_info["table_name"], \ sp_col_info["column_name"] # Check if the layer has already been layer_item = sp_col_info.get('item', None) layer_name = self.geom_col_layer_name( table_name, layer_item ) if layer_name in self._map_registry_layer_names(): layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0] self.iface.setActiveLayer(layer) return self.curr_lyr_table = table_name self.curr_lyr_sp_col = spatial_column if not layer_item is None: if isinstance(layer_item, str) or isinstance(layer_item, unicode): layer_name = layer_item else: layer_name = layer_item.layer_display() entity = self._curr_profile.entity_by_name(table_name) if entity is not None: geom_col_obj = entity.columns[spatial_column] srid = None if geom_col_obj.srid >= 100000: srid = geom_col_obj.srid curr_layer = vector_layer( table_name, geom_column=spatial_column, layer_name=layer_name, proj_wkt=srid ) else: curr_layer = vector_layer( table_name, geom_column=spatial_column, layer_name=layer_name, proj_wkt=None ) # for lookup layer. else: curr_layer = vector_layer( table_name, geom_column=spatial_column ) if curr_layer.isValid(): if curr_layer.name() in self._map_registry_layer_names(): return QgsMapLayerRegistry.instance().addMapLayer( curr_layer ) self.zoom_to_layer() self.onLayerAdded.emit(spatial_column, curr_layer) self.toggle_entity_multi_layers(curr_layer) self.set_canvas_crs(curr_layer) # Required in order for the layer name to be set if layer_name is not None: QTimer.singleShot( 100, lambda: self._set_layer_display_name( curr_layer, layer_name ) ) entity = self._curr_profile.entity_by_name(self.curr_lyr_table) fk_fields = self.join_fk_layer(curr_layer, entity) if entity is not None: self.sort_joined_columns(curr_layer, fk_fields) self.set_field_alias(curr_layer, entity, fk_fields) else: msg = QApplication.translate( "Spatial Unit Manager", "'{0}.{1}' layer is invalid, it cannot " "be added to the map view.".format( table_name, spatial_column ) ) QMessageBox.critical( self.iface.mainWindow(), 'Spatial Unit Manager', msg ) def set_field_alias(self, layer, entity, fk_fields): """ Set the field alia for fk joined fields so that they are same as the child columns. :param layer: The layer containing the join :type layer: QgsVectorLayer :param entity: The entity of the layer :type entity: Object :param fk_fields: The dictionary containing the parent and child fields :type fk_fields: OrderedDict :return: :rtype: """ for column, fk_field in fk_fields.iteritems(): header = entity.columns[column].header() f_index = layer.fieldNameIndex( u'{} {}'.format(header, fk_field) ) alias = u'{} Value'.format(header) layer.addAttributeAlias(f_index, alias) def zoom_to_layer(self): """ Zooms the map canvas to the extent the active layer. :return: :rtype: """ layer = self.iface.activeLayer() if not layer is None: self.iface.mapCanvas().setExtent( layer.extent() ) self.iface.mapCanvas().refresh() def geom_columns(self, entity): """ Returns the geometry columns of an entity. :param entity: The entity object. :type entity: Object :return: List of Geometry column objects :rtype: List """ geom_column = [ column for column in entity.columns.values() if column.TYPE_INFO == 'GEOMETRY' ] return geom_column def same_entity_layers(self): """ Returns layer names of an entity if they are more than one in one entity. :return: List in a list :rtype: List """ entity_layers = [] for entity in self.geom_entities: layer_list = self.entity_layer_names(entity) entity_layers.append(layer_list) return entity_layers def entity_layer_names(self, entity): """ Returns layer names of an entity if they are more than one in one entity. :param entity: The Entity object :param type: Object :return: List in a list :rtype: List """ cols = self.geom_columns(entity) layer_list = [] for col in cols: lyr_name = self.geom_col_layer_name( entity.name, col ) layer_list.append(lyr_name) return layer_list def layer_entity_children(self, sel_lyr_name): layer_lists = [ layer_list for layer_list in self.same_entity_layers() if sel_lyr_name in layer_list ] str_view = self._curr_profile.social_tenure.view_name if len(layer_lists) < 1: geom_columns = table_column_names( str_view, True ) layer_lists = [geom_columns] return layer_lists def toggle_entity_multi_layers(self, new_layer): """ Removes other layers created from the entity of the new layer. :param new_layer: The new layer added :type new_layer: QgsVectorLayer :return: None :rtype: NoneType """ sel_lyr_name = new_layer.name() layer_lists = self.layer_entity_children( sel_lyr_name ) # Include layers for toggling whose entity # has more than one geometry if len(layer_lists) < 1: return # if the layer_list is the # parent of selected layer for layer_name in layer_lists[0]: # remove other layers # of the same entity if layer_name != sel_lyr_name: layer_objects = QgsMapLayerRegistry. \ instance().mapLayersByName(layer_name) if len(layer_objects) > 0: for layer in layer_objects: layer_id = layer.id() QgsMapLayerRegistry. \ instance().removeMapLayer(layer_id) # Change the crs of the canvas based on the new layer layer_list = QgsMapLayerRegistry.instance(). \ mapLayersByName(sel_lyr_name) if len(layer_list) > 0: self.set_canvas_crs( layer_list[0] ) def _set_layer_display_name(self, layer, name): try: layer.setName(name) except RuntimeError: pass def _layer_table_column(self, layer): # Returns the table and column name # that a layer belongs to. table, column = '', '' if hasattr(layer, 'dataProvider'): if layer.dataProvider().name() == 'postgres': layerConnStr = layer.dataProvider().dataSourceUri() dataSourceURI = QgsDataSourceURI(layerConnStr) table, column = dataSourceURI.table(), \ dataSourceURI.geometryColumn() return table, column def _map_registry_layer_names(self): """ Returns a list of layers names. """ layers = QgsMapLayerRegistry.instance().mapLayers() layer_names = [lyr.name() for lyr in layers.values()] return layer_names @pyqtSignature("") def on_set_display_name_button_clicked(self): """ Method to change display name """ layer_names_ids = pg_layerNamesIDMapping().reverse layer = self.iface.activeLayer() if not layer is None: table_name = layer_names_ids.get(layer.id(), '') if table_name: # Check if the table name is in the current profile if table_name in self._profile_spatial_layers: prompt = \ u"Set the display name for '{0}' layer".format( layer.name() ) display_name, ok = QInputDialog.getText( self, 'Spatial Unit ' 'Manager', prompt ) if ok and display_name: # Get layer table and columns names table, column = self._layer_table_column(layer) if table and column: idx, layer_info = \ self._layer_info_from_table_column( table, column ) # Get item in the combo corresponding to the layer if idx != -1: self.stdm_layers_combo.setItemText( idx, display_name ) layer.setName(display_name) # Update configuration item config_item = layer_info.get('item', None) if not config_item is None: config_item.layer_display_name = \ display_name # Update configuration save_configuration() else: msg = QApplication.translate( "Spatial Unit Manager", u"The layer does not " u"belong in the '{0}' " u"profile.\nThe display name " u"will not be set." u"".format( self._curr_profile.name ) ) QMessageBox.critical( self.iface.mainWindow(), 'Spatial Unit Manager', msg ) def active_layer_source(self): """ Get the layer table name if the source is from the database. :return: The a Boolean True if a valid source is found or False if not. Alternatively, None if there is no active layer. :rtype: Boolean or NoneType """ active_layer = self.iface.activeLayer() return self.layer_source(active_layer) def layer_source(self, layer): """ Gets the layer source. :param layer: The layer :type layer: Any :return: Layer Source or None :rtype: """ if layer is None: return None source = layer.source() if source is None: return False source_value = dict(re.findall('(\S+)="?(.*?)"? ', source)) try: table = source_value['table'].split('.') table_name = table[1].strip('"') if table_name in pg_views(): return False entity = self._curr_profile.entity_by_name(table_name) if entity is None: return False else: self.active_entity = entity self.active_table = table_name # get all spatial columns of the entity. spatial_columns = [ c.name for c in entity.columns.values() if c.TYPE_INFO == 'GEOMETRY' ] # get all fields excluding the geometry. layer_fields = [ field.name() for field in layer.pendingFields() ] # get the currently being used geometry column active_sp_cols = [ col for col in spatial_columns if col not in layer_fields ] if len(active_sp_cols) == 1: self.active_sp_col = active_sp_cols[0] return True except KeyError: return False @pyqtSignature("") def on_import_gpx_file_button_clicked(self): """ Method to load GPS dialog """ source_status = self.active_layer_source() layer_map = QgsMapLayerRegistry.instance().mapLayers() error_title = QApplication.translate( 'SpatialUnitManagerDockWidget', 'GPS Feature Import Loading Error' ) if len(layer_map) > 0: if source_status is None: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have not selected a layer.\n ' 'Please select a valid layer to import GPS features.' ) ) elif source_status is False: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You have selected a non-STDM entity layer.\n ' 'Please select a valid layer to import GPS features.' ) ) elif source_status is True: self.gps_tool_dialog = GPSToolDialog( self.iface, self.active_entity, self.active_table, self.active_sp_col ) self.gps_tool_dialog.show() else: QMessageBox.critical( self, error_title, QApplication.translate( 'SpatialUnitManagerDockWidget', 'You must add an entity layer from Spatial Unit Manager\n' 'and select it to import GPS Features.' ) ) def _valid_entity(self): """ Checks if the current active layer in the layer panel represents a valid entity in the current profile :return: Error object :rtype: Object """ entity_profile = current_profile() entity_obj = entity_profile.entity_by_name(self.curr_lyr_table) if entity_obj is None: return None return entity_obj def closeEvent(self, event): """ On close of the dock window, this event is executed to run close_dock method :param event: The close event :type QCloseEvent :return: None """ self._plugin.spatialLayerManager.setChecked(False)