class SetPointMapTool(QgsMapToolEmitPoint): def __init__(self, canvas): self.canvas = canvas QgsMapToolEmitPoint.__init__(self, self.canvas) self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(Qt.red) self.vertexMarker.setCursor(Qt.CrossCursor) self.reset() def reset(self): self.centerPoint = None self.vertexMarker.hide() #self.isEmittingPoint = False #self.rubberBand.reset(QGis.Polygon) def canvasPressEvent(self, e): self.centerPoint = self.toMapCoordinates(e.pos()) self.vertexMarker.setCenter(self.centerPoint) if not self.vertexMarker.isVisible(): self.vertexMarker.show() #self.endPoint = self.startPoint #self.isEmittingPoint = True #self.showRect(self.startPoint, self.endPoint) def canvasReleaseEvent(self, e): return #self.isEmittingPoint = False # r = self.rectangle() #if r is not None: # print "Rectangle:", r.xMinimum(), r.yMinimum(), r.xMaximum(), r.yMaximum() def canvasMoveEvent(self, e): return #if not self.isEmittingPoint: #return #self.endPoint = self.toMapCoordinates(e.pos()) #self.showRect(self.startPoint, self.endPoint) def rectangle(self): if self.startPoint is None or self.endPoint is None: return None elif self.startPoint.x() == self.endPoint.x() or self.startPoint.y( ) == self.endPoint.y(): return None return QgsRectangle(self.startPoint, self.endPoint) def deactivate(self): super(SetPointMapTool, self).deactivate() #self.emit(SIGNAL("deactivated()")) self.deactivated.emit()
class StartEndMarker(object): def __init__(self, canvas, waypoints, color): self.canvas = canvas self.color = color self.start_vertex_marker = QgsVertexMarker(self.canvas) self.end_vertex_marker = QgsVertexMarker(self.canvas) self.update_markers(waypoints) def update_markers(self, steps): """ Update start and end vertex markers on canvas. :param steps: waypoints in the mission :type: list """ if len(steps) > 1: self.start_vertex_marker.setCenter(QgsPointXY(steps[0].x(), steps[0].y())) self.start_vertex_marker.setColor(self.color) self.start_vertex_marker.setIconSize(14) self.start_vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) # ICON_BOX, ICON_CROSS, ICON_X self.start_vertex_marker.setPenWidth(2) self.start_vertex_marker.show() self.end_vertex_marker.setCenter(QgsPointXY(steps[-1].x(), steps[-1].y())) self.end_vertex_marker.setColor(self.color) self.end_vertex_marker.setIconSize(14) self.end_vertex_marker.setIconType(QgsVertexMarker.ICON_BOX) # ICON_BOX, ICON_CROSS, ICON_X self.end_vertex_marker.setPenWidth(2) self.end_vertex_marker.show() else: self.start_vertex_marker.hide() self.end_vertex_marker.hide() def hide_markers(self): """ hide vertex markers. """ self.start_vertex_marker.hide() self.end_vertex_marker.hide() def close_markers(self): """ Remove vertex markers from canvas.""" self.hide_markers() self.canvas.scene().removeItem(self.start_vertex_marker) self.canvas.scene().removeItem(self.end_vertex_marker) self.start_vertex_marker = None self.end_vertex_marker = None
class ParentManage(ParentAction, object): def __init__(self, iface, settings, controller, plugin_dir): """ Class to keep common functions of classes 'ManageDocument', 'ManageElement' and 'ManageVisit' of toolbar 'edit' """ super(ParentManage, self).__init__(iface, settings, controller, plugin_dir) self.x = "" self.y = "" self.canvas = self.iface.mapCanvas() self.plan_om = None self.previous_map_tool = None self.autocommit = True self.lazy_widget = None self.workcat_id_end = None self.xyCoordinates_conected = False self.remove_ids = True self.snapper_manager = None def reset_lists(self): """ Reset list of selected records """ self.ids = [] self.list_ids = {} self.list_ids['arc'] = [] self.list_ids['node'] = [] self.list_ids['connec'] = [] self.list_ids['gully'] = [] self.list_ids['element'] = [] def reset_layers(self): """ Reset list of layers """ self.layers = {} self.layers['arc'] = [] self.layers['node'] = [] self.layers['connec'] = [] self.layers['gully'] = [] self.layers['element'] = [] def reset_model(self, dialog, table_object, geom_type): """ Reset model of the widget """ table_relation = table_object + "_x_" + geom_type widget_name = "tbl_" + table_relation widget = utils_giswater.getWidget(dialog, widget_name) if widget: widget.setModel(None) def remove_selection(self, remove_groups=True): """ Remove all previous selections """ layer = self.controller.get_layer_by_tablename("v_edit_arc") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_node") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_connec") if layer: layer.removeSelection() layer = self.controller.get_layer_by_tablename("v_edit_element") if layer: layer.removeSelection() if self.project_type == 'ud': layer = self.controller.get_layer_by_tablename("v_edit_gully") if layer: layer.removeSelection() try: if remove_groups: for layer in self.layers['arc']: layer.removeSelection() for layer in self.layers['node']: layer.removeSelection() for layer in self.layers['connec']: layer.removeSelection() for layer in self.layers['gully']: layer.removeSelection() for layer in self.layers['element']: layer.removeSelection() except: pass self.canvas.refresh() def reset_widgets(self, dialog, table_object): """ Clear contents of input widgets """ if table_object == "doc": utils_giswater.setWidgetText(dialog, "doc_type", "") utils_giswater.setWidgetText(dialog, "observ", "") utils_giswater.setWidgetText(dialog, "path", "") elif table_object == "element": utils_giswater.setWidgetText(dialog, "elementcat_id", "") utils_giswater.setWidgetText(dialog, "state", "") utils_giswater.setWidgetText(dialog, "expl_id","") utils_giswater.setWidgetText(dialog, "ownercat_id", "") utils_giswater.setWidgetText(dialog, "location_type", "") utils_giswater.setWidgetText(dialog, "buildercat_id", "") utils_giswater.setWidgetText(dialog, "workcat_id", "") utils_giswater.setWidgetText(dialog, "workcat_id_end", "") utils_giswater.setWidgetText(dialog, "comment", "") utils_giswater.setWidgetText(dialog, "observ", "") utils_giswater.setWidgetText(dialog, "path", "") utils_giswater.setWidgetText(dialog, "rotation", "") utils_giswater.setWidgetText(dialog, "verified", "") utils_giswater.setWidgetText(dialog, dialog.num_elements, "") def fill_widgets(self, dialog, table_object, row): """ Fill input widgets with data int he @row """ if table_object == "doc": utils_giswater.setWidgetText(dialog, "doc_type", row["doc_type"]) utils_giswater.setWidgetText(dialog, "observ", row["observ"]) utils_giswater.setWidgetText(dialog, "path", row["path"]) elif table_object == "element": state = "" if row['state']: sql = (f"SELECT name FROM value_state" f" WHERE id = '{row['state']}'") row_aux = self.controller.get_row(sql, commit=self.autocommit) if row_aux: state = row_aux[0] expl_id = "" if row['expl_id']: sql = (f"SELECT name FROM exploitation" f" WHERE expl_id = '{row['expl_id']}'") row_aux = self.controller.get_row(sql, commit=self.autocommit) if row_aux: expl_id = row_aux[0] utils_giswater.setWidgetText(dialog, "code", row['code']) sql = (f"SELECT elementtype_id FROM cat_element" f" WHERE id = '{row['elementcat_id']}'") row_type = self.controller.get_row(sql) if row_type: utils_giswater.setWidgetText(dialog, "element_type", row_type[0]) utils_giswater.setWidgetText(dialog, "elementcat_id", row['elementcat_id']) utils_giswater.setWidgetText(dialog, "num_elements", row['num_elements']) utils_giswater.setWidgetText(dialog, "state", state) utils_giswater.setWidgetText(dialog, "expl_id", expl_id) utils_giswater.setWidgetText(dialog, "ownercat_id", row['ownercat_id']) utils_giswater.setWidgetText(dialog, "location_type", row['location_type']) utils_giswater.setWidgetText(dialog, "buildercat_id", row['buildercat_id']) utils_giswater.setWidgetText(dialog, "builtdate", row['builtdate']) utils_giswater.setWidgetText(dialog, "workcat_id", row['workcat_id']) utils_giswater.setWidgetText(dialog, "workcat_id_end", row['workcat_id_end']) utils_giswater.setWidgetText(dialog, "comment", row['comment']) utils_giswater.setWidgetText(dialog, "observ", row['observ']) utils_giswater.setWidgetText(dialog, "link", row['link']) utils_giswater.setWidgetText(dialog, "verified", row['verified']) utils_giswater.setWidgetText(dialog, "rotation", row['rotation']) if str(row['undelete']) == 'True': dialog.undelete.setChecked(True) def get_records_geom_type(self, dialog, table_object, geom_type): """ Get records of @geom_type associated to selected @table_object """ object_id = utils_giswater.getWidgetText(dialog, table_object + "_id") table_relation = table_object + "_x_" + geom_type widget_name = "tbl_" + table_relation exists = self.controller.check_table(table_relation) if not exists: self.controller.log_info(f"Not found: {table_relation}") return sql = (f"SELECT {geom_type}_id " f"FROM {table_relation} " f"WHERE {table_object}_id = '{object_id}'") rows = self.controller.get_rows(sql, commit=True, log_info=False) if rows: for row in rows: self.list_ids[geom_type].append(str(row[0])) self.ids.append(str(row[0])) expr_filter = self.get_expr_filter(geom_type) self.set_table_model(dialog, widget_name, geom_type, expr_filter) def exist_object(self, dialog, table_object): """ Check if selected object (document or element) already exists """ # Reset list of selected records self.reset_lists() field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" object_id = utils_giswater.getWidgetText(dialog, table_object + "_id") # Check if we already have data with selected object_id sql = (f"SELECT * " f" FROM {table_object}" f" WHERE {field_object_id} = '{object_id}'") row = self.controller.get_row(sql, log_info=False) # If object_id not found: Clear data if not row: self.reset_widgets(dialog, table_object) if table_object == 'element': self.set_combo(dialog, 'state', 'value_state', 'state_vdefault', field_name='name') self.set_combo(dialog, 'expl_id', 'exploitation', 'exploitation_vdefault', field_id='expl_id',field_name='name') self.set_calendars(dialog, 'builtdate', 'config_param_user', 'value', 'builtdate_vdefault') self.set_combo(dialog, 'workcat_id', 'cat_work', 'workcat_vdefault', field_id='id', field_name='id') if hasattr(self, 'single_tool_mode'): # some tools can work differently if standalone or integrated in # another tool if self.single_tool_mode: self.remove_selection(True) else: self.remove_selection(True) self.reset_model(dialog, table_object, "arc") self.reset_model(dialog, table_object, "node") self.reset_model(dialog, table_object, "connec") self.reset_model(dialog, table_object, "element") if self.project_type == 'ud': self.reset_model(dialog, table_object, "gully") return # Fill input widgets with data of the @row self.fill_widgets(dialog, table_object, row) # Check related 'arcs' self.get_records_geom_type(dialog, table_object, "arc") # Check related 'nodes' self.get_records_geom_type(dialog, table_object, "node") # Check related 'connecs' self.get_records_geom_type(dialog, table_object, "connec") # Check related 'elements' self.get_records_geom_type(dialog, table_object, "element") # Check related 'gullys' if self.project_type == 'ud': self.get_records_geom_type(dialog, table_object, "gully") def populate_combo(self, dialog, widget, table_name, field_name="id"): """ Executes query and fill combo box """ sql = (f"SELECT {field_name}" f" FROM {table_name}" f" ORDER BY {field_name}") rows = self.controller.get_rows(sql, commit=self.autocommit) utils_giswater.fillComboBox(dialog, widget, rows) if rows: utils_giswater.setCurrentIndex(dialog, widget, 0) def set_combo(self, dialog, widget, table_name, parameter, field_id='id', field_name='id'): """ Executes query and set combo box """ sql = (f"SELECT t1.{field_name} FROM {table_name} as t1" f" INNER JOIN config_param_user as t2 ON t1.{field_id}::text = t2.value::text" f" WHERE parameter = '{parameter}' AND cur_user = current_user") row = self.controller.get_row(sql) if row: utils_giswater.setWidgetText(dialog, widget, row[0]) def set_calendars(self, dialog, widget, table_name, value, parameter): """ Executes query and set QDateEdit """ sql = (f"SELECT {value} FROM {table_name}" f" WHERE parameter = '{parameter}' AND cur_user = current_user") row = self.controller.get_row(sql) if row: date = QDate.fromString(row[0], 'yyyy-MM-dd') else: date = QDate.currentDate() utils_giswater.setCalendarDate(dialog, widget, date) def add_point(self): """ Create the appropriate map tool and connect to the corresponding signal """ active_layer = self.iface.activeLayer() if active_layer is None: active_layer = self.controller.get_layer_by_tablename('version') self.iface.setActiveLayer(active_layer) # Vertex marker self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setColor(QColor(255, 100, 255)) self.vertex_marker.setIconSize(15) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CROSS) self.vertex_marker.setPenWidth(3) # Snapper if self.snapper_manager is None: self.snapper_manager = SnappingConfigManager(self.iface) self.snapper = self.snapper_manager.get_snapper() if self.snapper_manager.controller is None: self.snapper_manager.set_controller(self.controller) self.emit_point = QgsMapToolEmitPoint(self.canvas) self.previous_map_tool = self.canvas.mapTool() self.canvas.setMapTool(self.emit_point) self.canvas.xyCoordinates.connect(self.mouse_move) self.xyCoordinates_conected = True self.emit_point.canvasClicked.connect(partial(self.get_xy)) def mouse_move(self, point): # Hide marker and get coordinates self.snapped_point = None self.vertex_marker.hide() event_point = self.snapper_manager.get_event_point(point=point) # Snapping result = self.snapper_manager.snap_to_background_layers(event_point) if self.snapper_manager.result_is_valid(): self.snapper_manager.add_marker(result, self.vertex_marker) else: self.vertex_marker.hide() def get_xy(self, point): """ Get coordinates of selected point """ if self.snapped_point: self.x = self.snapped_point.x() self.y = self.snapped_point.y() else: self.x = point.x() self.y = point.y() message = "Geometry has been added!" self.controller.show_info(message) self.emit_point.canvasClicked.disconnect() self.canvas.xyCoordinates.disconnect() self.xyCoordinates_conected = False self.iface.mapCanvas().refreshAllLayers() self.vertex_marker.hide() def get_values_from_form(self, dialog): self.enddate = utils_giswater.getCalendarDate(dialog, "enddate") self.workcat_id_end = utils_giswater.getWidgetText(dialog, "workcat_id_end") self.description = utils_giswater.getWidgetText(dialog, "descript") def tab_feature_changed(self, dialog, table_object): """ Set geom_type and layer depending selected tab @table_object = ['doc' | 'element' | 'cat_work'] """ self.get_values_from_form(dialog) tab_position = dialog.tab_feature.currentIndex() if tab_position == 0: self.geom_type = "arc" elif tab_position == 1: self.geom_type = "node" elif tab_position == 2: self.geom_type = "connec" elif tab_position == 3: self.geom_type = "element" elif tab_position == 4: self.geom_type = "gully" self.hide_generic_layers() widget_name = f"tbl_{table_object}_x_{self.geom_type}" viewname = f"v_edit_{self.geom_type}" self.widget = utils_giswater.getWidget(dialog, widget_name) # Adding auto-completion to a QLineEdit self.set_completer_feature_id(dialog.feature_id, self.geom_type, viewname) self.iface.actionPan().trigger() def set_completer_object(self, dialog, table_object): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ widget = utils_giswater.getWidget(dialog, table_object + "_id") if not widget: return # Set SQL field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" sql = (f"SELECT DISTINCT({field_object_id})" f" FROM {table_object}") rows = self.controller.get_rows(sql, commit=self.autocommit) if rows is None: return for i in range(0, len(rows)): aux = rows[i] rows[i] = str(aux[0]) # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(rows) self.completer.setModel(model) def set_completer_widget(self, tablename, widget, field_id): """ Set autocomplete of widget @table_object + "_id" getting id's from selected @table_object """ if not widget: return # Set SQL sql = (f"SELECT DISTINCT({field_id})" f" FROM {tablename}" f" ORDER BY {field_id}") row = self.controller.get_rows(sql, commit=True) for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) # Set completer and model: add autocomplete in the widget self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() model.setStringList(row) self.completer.setModel(model) def set_completer_feature_id(self, widget, geom_type, viewname): """ Set autocomplete of widget 'feature_id' getting id's from selected @viewname """ # Adding auto-completion to a QLineEdit self.completer = QCompleter() self.completer.setCaseSensitivity(Qt.CaseInsensitive) widget.setCompleter(self.completer) model = QStringListModel() sql = (f"SELECT {geom_type}_id" f" FROM {viewname}") row = self.controller.get_rows(sql, commit=self.autocommit) if row: for i in range(0, len(row)): aux = row[i] row[i] = str(aux[0]) model.setStringList(row) self.completer.setModel(model) def get_expr_filter(self, geom_type): """ Set an expression filter with the contents of the list. Set a model with selected filter. Attach that model to selected table """ list_ids = self.list_ids[geom_type] field_id = geom_type + "_id" if len(list_ids) == 0: return None # Set expression filter with features in the list expr_filter = field_id + " IN (" for i in range(len(list_ids)): expr_filter += f"'{list_ids[i]}', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) if not is_valid: return None # Select features of layers applying @expr self.select_features_by_ids(geom_type, expr) return expr_filter def reload_table(self, dialog, table_object, geom_type, expr_filter): """ Reload @widget with contents of @tablename applying selected @expr_filter """ if type(table_object) is str: widget_name = f"tbl_{table_object}_x_{geom_type}" widget = utils_giswater.getWidget(dialog, widget_name) if not widget: message = "Widget not found" self.controller.log_info(message, parameter=widget_name) return None elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return None expr = self.set_table_model(dialog, widget, geom_type, expr_filter) return expr def set_table_model(self, dialog, table_object, geom_type, expr_filter): """ Sets a TableModel to @widget_name attached to @table_name and filter @expr_filter """ expr = None if expr_filter: # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return expr # Set a model with selected filter expression table_name = "v_edit_" + geom_type if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set the model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.select() if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) return expr # Attach model to selected widget if type(table_object) is str: widget = utils_giswater.getWidget(dialog, table_object) if not widget: message = "Widget not found" self.controller.log_info(message, parameter=table_object) return expr elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return expr if expr_filter: widget.setModel(model) widget.model().setFilter(expr_filter) widget.model().select() else: widget.setModel(None) return expr def apply_lazy_init(self, widget): """Apply the init function related to the model. It's necessary a lazy init because model is changed everytime is loaded.""" if self.lazy_widget is None: return if widget != self.lazy_widget: return self.lazy_init_function(self.lazy_widget) def lazy_configuration(self, widget, init_function): """set the init_function where all necessary events are set. This is necessary to allow a lazy setup of the events because set_table_events can create a table with a None model loosing any event connection.""" # TODO: create a dictionary with key:widged.objectName value:initFuction # to allow multiple lazy initialization self.lazy_widget = widget self.lazy_init_function = init_function def select_features_by_ids(self, geom_type, expr): """ Select features of layers of group @geom_type applying @expr """ # Build a list of feature id's and select them for layer in self.layers[geom_type]: if expr is None: layer.removeSelection() else: it = layer.getFeatures(QgsFeatureRequest(expr)) id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) else: layer.removeSelection() def delete_records(self, dialog, table_object, query=False): """ Delete selected elements of the table """ self.disconnect_signal_selection_changed() if type(table_object) is str: widget_name = f"tbl_{table_object}_x_{self.geom_type}" widget = utils_giswater.getWidget(dialog, widget_name) if not widget: message = "Widget not found" self.controller.show_warning(message, parameter=widget_name) return elif type(table_object) is QTableView: widget = table_object else: message = "Table_object is not a table name or QTableView" self.controller.log_info(message) return # Control when QTableView is void or has no model try: # Get selected rows selected_list = widget.selectionModel().selectedRows() except AttributeError as e: selected_list = [] if len(selected_list) == 0: message = "Any record selected" self.controller.show_info_box(message) return if query: full_list = widget.model() for x in range(0, full_list.rowCount()): self.ids.append(widget.model().record(x).value(f"{self.geom_type}_id")) else: self.ids = self.list_ids[self.geom_type] field_id = self.geom_type + "_id" del_id = [] inf_text = "" list_id = "" for i in range(0, len(selected_list)): row = selected_list[i].row() id_feature = widget.model().record(row).value(field_id) inf_text += f"{id_feature}, " list_id += f"'{id_feature}', " del_id.append(id_feature) inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: for el in del_id: self.ids.remove(el) else: return expr_filter = None expr = None if len(self.ids) > 0: # Set expression filter with features in the list expr_filter = f'"{field_id}" IN (' for i in range(len(self.ids)): expr_filter += f"'{self.ids[i]}', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return # Update model of the widget with selected expr_filter if query: self.delete_feature_at_plan(dialog, self.geom_type, list_id) self.reload_qtable(dialog, self.geom_type, self.plan_om) else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Select features with previous filter # Build a list of feature id's and select them self.select_features_by_ids(self.geom_type, expr) if query: self.remove_selection() # Update list self.list_ids[self.geom_type] = self.ids self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def manage_close(self, dialog, table_object, cur_active_layer=None): """ Close dialog and disconnect snapping """ if cur_active_layer: self.iface.setActiveLayer(cur_active_layer) if hasattr(self, 'single_tool_mode'): # some tools can work differently if standalone or integrated in # another tool if self.single_tool_mode: self.remove_selection(True) else: self.remove_selection(True) self.reset_model(dialog, table_object, "arc") self.reset_model(dialog, table_object, "node") self.reset_model(dialog, table_object, "connec") self.reset_model(dialog, table_object, "element") if self.project_type == 'ud': self.reset_model(dialog, table_object, "gully") self.close_dialog(dialog) self.hide_generic_layers() self.disconnect_snapping() self.disconnect_signal_selection_changed() def selection_init(self, dialog, table_object, query=False): """ Set canvas map tool to an instance of class 'MultipleSelection' """ multiple_selection = MultipleSelection(self.iface, self.controller, self.layers[self.geom_type], parent_manage=self, table_object=table_object, dialog=dialog) self.disconnect_signal_selection_changed() self.previous_map_tool = self.canvas.mapTool() self.canvas.setMapTool(multiple_selection) self.connect_signal_selection_changed(dialog, table_object, query) cursor = self.get_cursor_multiple_selection() self.canvas.setCursor(cursor) def selection_changed(self, dialog, table_object, geom_type, query=False): """ Slot function for signal 'canvas.selectionChanged' """ self.disconnect_signal_selection_changed() field_id = geom_type + "_id" if self.remove_ids: self.ids = [] # Iterate over all layers of the group for layer in self.layers[self.geom_type]: if layer.selectedFeatureCount() > 0: # Get selected features of the layer features = layer.selectedFeatures() for feature in features: # Append 'feature_id' into the list selected_id = feature.attribute(field_id) if selected_id not in self.ids: self.ids.append(selected_id) if geom_type == 'arc': self.list_ids['arc'] = self.ids elif geom_type == 'node': self.list_ids['node'] = self.ids elif geom_type == 'connec': self.list_ids['connec'] = self.ids elif geom_type == 'gully': self.list_ids['gully'] = self.ids elif geom_type == 'element': self.list_ids['element'] = self.ids expr_filter = None if len(self.ids) > 0: # Set 'expr_filter' with features that are in the list expr_filter = f'"{field_id}" IN (' for i in range(len(self.ids)): expr_filter += f"'{self.ids[i]}', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) #@UnusedVariable if not is_valid: return self.select_features_by_ids(geom_type, expr) # Reload contents of table 'tbl_@table_object_x_@geom_type' if query: self.insert_feature_to_plan(dialog, self.geom_type) if self.plan_om == 'plan': self.remove_selection() self.reload_qtable(dialog, geom_type, self.plan_om) else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Remove selection in generic 'v_edit' layers if self.plan_om == 'plan': self.remove_selection(False) self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def delete_feature_at_plan(self, dialog, geom_type, list_id): """ Delete features_id to table plan_@geom_type_x_psector""" value = utils_giswater.getWidgetText(dialog, dialog.psector_id) sql = (f"DELETE FROM {self.plan_om}_psector_x_{geom_type} " f"WHERE {geom_type}_id IN ({list_id}) AND psector_id = '{value}'") self.controller.execute_sql(sql) def enable_feature_type(self, dialog): feature_type = dialog.findChild(QComboBox, 'feature_type') table = dialog.findChild(QTableView, 'tbl_relation') if feature_type is not None and table is not None: if len(self.ids) > 0: feature_type.setEnabled(False) else: feature_type.setEnabled(True) def insert_feature(self, dialog, table_object, query=False, remove_ids=True): """ Select feature with entered id. Set a model with selected filter. Attach that model to selected table """ self.disconnect_signal_selection_changed() # Clear list of ids if remove_ids: self.ids = [] field_id = self.geom_type + "_id" feature_id = utils_giswater.getWidgetText(dialog, "feature_id") if feature_id == 'null': message = "You need to enter a feature id" self.controller.show_info_box(message) return # Iterate over all layers of the group for layer in self.layers[self.geom_type]: if layer.selectedFeatureCount() > 0: # Get selected features of the layer features = layer.selectedFeatures() for feature in features: # Append 'feature_id' into the list selected_id = feature.attribute(field_id) if selected_id not in self.ids: self.ids.append(selected_id) if feature_id not in self.ids: # If feature id doesn't exist in list -> add self.ids.append(str(feature_id)) # Set expression filter with features in the list expr_filter = f'"{field_id}" IN (' for i in range(len(self.ids)): expr_filter += f"'{self.ids[i]}', " expr_filter = expr_filter[:-2] + ")" # Check expression (is_valid, expr) = self.check_expression(expr_filter) if not is_valid: return # Select features with previous filter # Build a list of feature id's and select them for layer in self.layers[self.geom_type]: it = layer.getFeatures(QgsFeatureRequest(expr)) id_list = [i.id() for i in it] if len(id_list) > 0: layer.selectByIds(id_list) # Reload contents of table 'tbl_???_x_@geom_type' if query: self.insert_feature_to_plan(dialog, self.geom_type) self.remove_selection() else: self.reload_table(dialog, table_object, self.geom_type, expr_filter) self.apply_lazy_init(table_object) # Update list self.list_ids[self.geom_type] = self.ids self.enable_feature_type(dialog) self.connect_signal_selection_changed(dialog, table_object) def insert_feature_to_plan(self, dialog, geom_type): """ Insert features_id to table plan_@geom_type_x_psector """ value = utils_giswater.getWidgetText(dialog, dialog.psector_id) for i in range(len(self.ids)): sql = (f"SELECT {geom_type}_id " f"FROM {self.plan_om}_psector_x_{geom_type} " f"WHERE {geom_type}_id = '{self.ids[i]}' AND psector_id = '{value}'") row = self.controller.get_row(sql) if not row: sql = (f"INSERT INTO {self.plan_om}_psector_x_{geom_type}" f"({geom_type}_id, psector_id) VALUES('{self.ids[i]}', '{value}')") self.controller.execute_sql(sql) self.reload_qtable(dialog, geom_type, self.plan_om) def reload_qtable(self, dialog, geom_type, plan_om): """ Reload QtableView """ value = utils_giswater.getWidgetText(dialog, dialog.psector_id) expr = f"psector_id = '{value}'" qtable = utils_giswater.getWidget(dialog, f'tbl_psector_x_{geom_type}') self.fill_table_by_expr(qtable, f"{plan_om}_psector_x_{geom_type}", expr) self.set_table_columns(dialog, qtable, f"{plan_om}_psector_x_{geom_type}") self.refresh_map_canvas() def fill_table_by_expr(self, qtable, table_name, expr): """ :param qtable: QTableView to show :param expr: expression to set model """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name model = QSqlTableModel() model.setTable(table_name) model.setFilter(expr) model.setEditStrategy(QSqlTableModel.OnFieldChange) qtable.setEditTriggers(QTableView.DoubleClicked) model.select() qtable.setModel(model) qtable.show() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) def disconnect_snapping(self): """ Select 'Pan' as current map tool and disconnect snapping """ try: self.iface.actionPan().trigger() self.canvas.xyCoordinates.disconnect() if self.emit_point: self.emit_point.canvasClicked.disconnect() except: pass def fill_table_object(self, widget, table_name, expr_filter=None): """ Set a model with selected filter. Attach that model to selected table """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model model = QSqlTableModel() model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.sort(0, 1) if expr_filter: model.setFilter(expr_filter) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view widget.setModel(model) def filter_by_id(self, dialog, widget_table, widget_txt, table_object, field_object_id='id'): field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" object_id = utils_giswater.getWidgetText(dialog, widget_txt) if object_id != 'null': expr = f"{field_object_id}::text ILIKE '%{object_id}%'" # Refresh model with selected filter widget_table.model().setFilter(expr) widget_table.model().select() else: self.fill_table_object(widget_table, self.schema_name + "." + table_object) def delete_selected_object(self, widget, table_object): """ Delete selected objects of the table (by object_id) """ # Get selected rows selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return inf_text = "" list_id = "" field_object_id = "id" if table_object == "element": field_object_id = table_object + "_id" elif "v_ui_om_visitman_x_" in table_object: field_object_id = "visit_id" for i in range(0, len(selected_list)): row = selected_list[i].row() id_ = widget.model().record(row).value(str(field_object_id)) inf_text += f"{id_}, " list_id += f"'{id_}', " inf_text = inf_text[:-2] list_id = list_id[:-2] message = "Are you sure you want to delete these records?" title = "Delete records" answer = self.controller.ask_question(message, title, inf_text) if answer: sql = (f"DELETE FROM {table_object} " f"WHERE {field_object_id} IN ({list_id})") self.controller.execute_sql(sql, commit=self.autocommit) widget.model().select() def open_selected_object(self, dialog, widget, table_object): """ Open object form with selected record of the table """ selected_list = widget.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return row = selected_list[0].row() # Get object_id from selected row field_object_id = "id" widget_id = table_object + "_id" if table_object == "element": field_object_id = table_object + "_id" if table_object == "v_ui_om_visit": widget_id = "visit_id" elif "v_ui_om_visitman_x_" in table_object: field_object_id = "visit_id" selected_object_id = widget.model().record(row).value(field_object_id) # Close this dialog and open selected object dialog.close() if table_object == "doc": self.manage_document() utils_giswater.setWidgetText(self.dlg_add_doc, widget_id, selected_object_id) elif table_object == "element": self.manage_element(new_element_id=False) utils_giswater.setWidgetText(self.dlg_add_element, widget_id, selected_object_id) elif table_object == "v_ui_om_visit": self.manage_visit(visit_id=selected_object_id) elif "v_ui_om_visitman_x_" in table_object: self.manage_visit(visit_id=selected_object_id) def set_selectionbehavior(self, dialog): # Get objects of type: QTableView widget_list = dialog.findChildren(QTableView) for widget in widget_list: widget.setSelectionBehavior(QAbstractItemView.SelectRows) def hide_generic_layers(self, visible=False): """ Hide generic layers """ layer = self.controller.get_layer_by_tablename("v_edit_arc") if layer: self.controller.set_layer_visible(layer) layer = self.controller.get_layer_by_tablename("v_edit_node") if layer: self.controller.set_layer_visible(layer) layer = self.controller.get_layer_by_tablename("v_edit_connec") if layer: self.controller.set_layer_visible(layer) layer = self.controller.get_layer_by_tablename("v_edit_element") if layer: self.controller.set_layer_visible(layer) if self.project_type == 'ud': layer = self.controller.get_layer_by_tablename("v_edit_gully") if layer: self.controller.set_layer_visible(layer) def connect_signal_selection_changed(self, dialog, table_object, query=False): """ Connect signal selectionChanged """ try: self.canvas.selectionChanged.connect(partial(self.selection_changed, dialog, table_object, self.geom_type, query)) except Exception: pass def disconnect_signal_selection_changed(self): """ Disconnect signal selectionChanged """ try: self.canvas.selectionChanged.disconnect() self.iface.actionPan().trigger() except Exception: pass def fill_widget_with_fields(self, dialog, data_object, field_names): """Fill the Widget with value get from data_object limited to the list of field_names.""" for field_name in field_names: value = getattr(data_object, field_name) if not hasattr(dialog, field_name): continue widget = getattr(dialog, field_name) if type(widget) == QDateEdit: widget.setDate(value if value else QDate.currentDate()) elif type(widget) == QDateTimeEdit: widget.setDateTime(value if value else QDateTime.currentDateTime()) if type(widget) in [QLineEdit, QTextEdit]: if value: widget.setText(value) else: widget.clear() if type(widget) in [QComboBox]: if not value: widget.setCurrentIndex(0) continue # look the value in item text index = widget.findText(str(value)) if index >= 0: widget.setCurrentIndex(index) continue # look the value in itemData index = widget.findData(value) if index >= 0: widget.setCurrentIndex(index) continue def set_model_to_table(self, widget, table_name, expr_filter): """ Set a model with selected filter. Attach that model to selected table """ if self.schema_name not in table_name: table_name = self.schema_name + "." + table_name # Set model model = QSqlTableModel(); model.setTable(table_name) model.setEditStrategy(QSqlTableModel.OnManualSubmit) model.setFilter(expr_filter) model.select() # Check for errors if model.lastError().isValid(): self.controller.show_warning(model.lastError().text()) # Attach model to table view if widget: widget.setModel(model) else: self.controller.log_info("set_model_to_table: widget not found")
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/gazetteersearch" # initialize locale localePath = "" if QGis.QGIS_VERSION_INT < 10900: locale = QSettings().value("locale/userLocale").toString()[0:2] else: locale = QSettings().value("locale/userLocale")[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search",self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): searchString = searchString.encode('utf-8') gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) def callback(data): try: self.results = list(gazetteer.parseRequestResults(data, self.iface)) except: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) common.search(url, callback) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteersearch.gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() if QGis.QGIS_VERSION_INT < 10900: src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) else: src_crs = QgsCoordinateReferenceSystem(res.epsg, QgsCoordinateReferenceSystem.EpsgCrsId) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x,y,x,y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class AddTankTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.snapper = None self.snapped_feat_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = False if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') if not self.mouse_clicked: # Mouse not clicked: snapping to closest vertex # (retval, result) = self.snapper.snapMapPoint(self.toMapCoordinates(event.pos())) # if len(result) > 0: # It's a vertex on an existing pipe match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # self.snapped_feat_id = result[0].snappedAtGeometry # snapped_vertex = result[0].snappedVertex # self.snapped_vertex_nr = result[0].snappedVertexNr # self.snapped_vertex = QgsPoint(snapped_vertex.x(), snapped_vertex.y()) self.snapped_feat_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: # It's a new, isolated vertex self.snapped_feat_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # Find first available ID for Tanks tank_eid = NetworkUtils.find_next_id(self.params.tanks_vlay, Tank.prefix) # TODO: softcode curve = self.data_dock.cbo_tank_curve.itemData( self.data_dock.cbo_tank_curve.currentIndex()) if curve is not None: tank_curve_id = curve.id else: tank_curve_id = None elev = 0 if self.elev is None and self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', Qgis.Warning, 5) # TODO: softcode else: elev = self.elev diameter = float(self.data_dock.txt_tank_diameter.text()) deltaz = float(self.data_dock.txt_tank_deltaz.text()) level_init = float(self.data_dock.txt_tank_level_init.text()) level_min = float(self.data_dock.txt_tank_level_min.text()) level_max = float(self.data_dock.txt_tank_level_max.text()) vol_min = float(self.data_dock.txt_tank_vol_min.text()) tank_desc = self.data_dock.txt_tank_desc.text() tank_tag = self.data_dock.cbo_tank_tag.currentText() # No links snapped: create a new stand-alone tank if self.snapped_feat_id is None: NodeHandler.create_new_tank(self.params, self.mouse_pt, tank_eid, tank_curve_id, diameter, elev, deltaz, level_init, level_min, level_max, vol_min, tank_desc, tank_tag) # A link has been snapped else: # Get the snapped pipe and split it request = QgsFeatureRequest().setFilterFid( self.snapped_feat_id) feats = list(self.params.pipes_vlay.getFeatures(request)) if len(feats) > 0: snapped_pipe = QgsFeature(feats[0]) (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes( self.params, snapped_pipe.geometry()) if start_node_ft is None or end_node_ft is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pipe is missing the start or end nodes. Cannot add a new tank along the pipe.', Qgis.Warning, 5) # TODO: softcode return # Check that the snapped point on line is distant enough from start/end nodes if start_node_ft.geometry().distance(QgsGeometry.fromPointXY(self.snapped_vertex)) > self.params.min_dist and\ end_node_ft.geometry().distance(QgsGeometry.fromPointXY(self.snapped_vertex)) > self.params.min_dist: LinkHandler.split_pipe(self.params, snapped_pipe, self.snapped_vertex) # New node on existing line NodeHandler.create_new_tank( self.params, self.snapped_vertex, tank_eid, tank_curve_id, diameter, self.elev, deltaz, level_init, level_min, level_max, vol_min, tank_desc, tank_tag) # Replace pipe start node with new tank elif start_node_ft.geometry().distance( QgsGeometry.fromPointXY(self.snapped_vertex)) <= 0: # Delete junction NodeHandler.delete_node(self.params, self.params.junctions_vlay, start_node_ft, False) # New node on existing line NodeHandler.create_new_tank( self.params, self.snapped_vertex, tank_eid, tank_curve_id, diameter, self.elev, deltaz, level_init, level_min, level_max, vol_min, tank_desc, tank_tag) # Replace pipe end node with new tank elif end_node_ft.geometry().distance( QgsGeometry.fromPointXY(self.snapped_vertex)) <= 0: # Delete junction NodeHandler.delete_node(self.params, self.params.junctions_vlay, end_node_ft, False) # New node on existing line NodeHandler.create_new_tank( self.params, self.snapped_vertex, tank_eid, tank_curve_id, diameter, self.elev, deltaz, level_init, level_min, level_max, vol_min, tank_desc, tank_tag) else: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The selected position is too close to a junction: cannon create the tank.', Qgis.Warning, 5) # TODO: softcode self.iface.mapCanvas().refresh() def activate(self): self.update_snapper() # Editing if not self.params.tanks_vlay.isEditable(): self.params.tanks_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() def deactivate(self): # QgsProject.instance().setSnapSettingsForLayer(self.params.pipes_vlay.id(), # True, # QgsSnapper.SnapToSegment, # QgsTolerance.MapUnits, # 0, # True) self.data_dock.btn_add_tank.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def update_snapper(self): layers = {self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Needed by Observable def update(self, observable): self.update_snapper()
class QGISRedEditLinksGeometryTool(QgsMapTool): ownMainLayers = ["Pipes", "Valves", "Pumps", "ServiceConnections"] def __init__(self, button, iface, projectDirectory, netwName): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.toolbarButton = button self.snapper = None self.vertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.vertexMarker.setColor(QColor(255, 87, 51)) self.vertexMarker.setIconSize(15) self.vertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(3) self.vertexMarker.hide() self.pipeSnapper = None self.pipeMarker = QgsVertexMarker(self.iface.mapCanvas()) self.pipeMarker.setColor(QColor(143, 0, 255)) self.pipeMarker.setIconSize(10) try: self.pipeMarker.setIconType( QgsVertexMarker.ICON_DOUBLE_TRIANGLE) # or ICON_CROSS, ICON_X except: self.pipeMarker.setIconType( QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.pipeMarker.setPenWidth(3) self.pipeMarker.hide() self.mouseClicked = False self.clickedPoint = None self.objectSnapped = None self.pipeSnapped = None self.selectedFeature = None self.selectedLayer = None self.newPositionVector = QgsVector(0, 0) self.rubberBand = None self.newVertexMarker = QgsVertexMarker(self.iface.mapCanvas()) self.newVertexMarker.setColor(QColor(55, 198, 5)) self.newVertexMarker.setIconSize(15) self.newVertexMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.newVertexMarker.setPenWidth(3) self.newVertexMarker.hide() def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) myLayers = [] # Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: myLayers.append(layer) if not layer.isEditable(): layer.startEditing() # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(2) # Vertex config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.snapper.setConfig(config) self.pipeSnapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.pipeSnapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(2) # Vertex config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.pipeSnapper.setConfig(config) def deactivate(self): self.toolbarButton.setChecked(False) # End Editing layers = self.getLayers() for layer in layers: openedLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if openedLayerPath == layerPath: if layer.isModified(): layer.commitChanges() else: layer.rollBack() def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def getUniformedPath(self, path): return QGISRedUtils().getUniformedPath(path) def getLayerPath(self, layer): return QGISRedUtils().getLayerPath(layer) def generatePath(self, folder, fileName): return QGISRedUtils().generatePath(folder, fileName) def getLayers(self): return QGISRedUtils().getLayers() def areOverlapedPoints(self, point1, point2): tolerance = 0.1 if point1.distance(point2) < tolerance: return True else: return False def isInPath(self, point1, point2, myPoint): width = point2.x() - point1.x() height = point2.y() - point1.y() widthM = myPoint.x() - point1.x() heightM = myPoint.y() - point1.y() if abs(width) >= abs(height): yEst = widthM * height / width + point1.y() if abs(yEst - myPoint.y()) < 1E-9: return True else: xEst = heightM * width / height + point1.x() if abs(xEst - myPoint.x()) < 1E-9: return True return False def createRubberBand(self, points): myPoints = points if isinstance(points[0], QgsPointXY): myPoints = [] for p in points: myPoints.append(QgsPoint(p.x(), p.y())) self.rubberBand = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand.setToGeometry(QgsGeometry.fromPolyline(myPoints), None) self.rubberBand.setColor(QColor(55, 198, 5)) self.rubberBand.setWidth(1) self.rubberBand.setLineStyle(Qt.DashLine) self.newVertexMarker.setCenter(QgsPointXY(points[0].x(), points[0].y())) self.newVertexMarker.show() def updateRubberBand(self): newX = self.clickedPoint.x() + self.newPositionVector.x() newY = self.clickedPoint.y() + self.newPositionVector.y() self.rubberBand.movePoint(1, QgsPointXY(newX, newY)) self.newVertexMarker.setCenter(QgsPointXY(newX, newY)) def moveVertexLink(self, layer, feature, newPosition, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.moveVertex(newPosition.x(), newPosition.y(), feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def deleteVertexLink(self, layer, feature, vertexIndex): if layer.isEditable(): layer.beginEditCommand("Update link geometry") try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.deleteVertex(feature.id(), vertexIndex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() def insertVertexLink(self, layer, feature, newPoint): if layer.isEditable(): layer.beginEditCommand("Update link geometry") vertex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part for i in range(len(part) - 1): if self.isInPath( QgsPointXY(part[i].x(), part[i].y()), QgsPointXY(part[i + 1].x(), part[i + 1].y()), newPoint): vertex = i + 1 try: edit_utils = QgsVectorLayerEditUtils(layer) edit_utils.insertVertex(newPoint.x(), newPoint.y(), feature.id(), vertex) except Exception as e: layer.destroyEditCommand() raise e layer.endEditCommand() """Events""" def canvasPressEvent(self, event): if self.objectSnapped is None: self.clickedPoint = None return if event.button() == Qt.RightButton: self.mouseClicked = False self.clickedPoint = None if event.button() == Qt.LeftButton: self.clickedPoint = self.objectSnapped.point() if self.vertexIndex == -1: return self.mouseClicked = True self.createRubberBand( [self.objectSnapped.point(), self.objectSnapped.point()]) def canvasMoveEvent(self, event): mousePoint = self.toMapCoordinates(event.pos()) # Mouse not clicked if not self.mouseClicked: self.pipeSnappedOn = False matchSnapper = self.snapper.snapToMap(mousePoint) if matchSnapper.isValid(): valid = False layer = matchSnapper.layer() snapLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_" + name + ".shp") if snapLayerPath == layerPath: valid = True if valid: self.objectSnapped = matchSnapper self.selectedLayer = layer vertex = matchSnapper.point() featureId = matchSnapper.featureId() request = QgsFeatureRequest().setFilterFid(featureId) nodes = list(layer.getFeatures(request)) self.selectedFeature = QgsFeature(nodes[0]) # #Ver aquí si es el nudo inicial y final middleNode = False self.vertexIndex = -1 if layer.geometryType() == 1: # Line featureGeometry = self.selectedFeature.geometry() if featureGeometry.isMultipart(): parts = featureGeometry.get() for part in parts: # only one part if middleNode: break i = -1 for v in part: i = i + 1 if ( i == 0 or i == len(part) - 1 ) and "ServiceConnections" not in snapLayerPath: continue matchedPoint = QgsPointXY( vertex.x(), vertex.y()) if self.areOverlapedPoints( QgsGeometry.fromPointXY( matchedPoint), QgsGeometry.fromPointXY( QgsPointXY(v.x(), v.y()))): middleNode = True self.vertexIndex = i if ( i == 0 or i == len(part) - 1 ) and "ServiceConnections" in snapLayerPath: self.pipeSnappedOn = True break if middleNode: self.vertexMarker.setCenter( QgsPointXY(vertex.x(), vertex.y())) self.vertexMarker.show() else: self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() else: self.objectSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexMarker.hide() # Mouse clicked else: # Snap pipe layer if self.pipeSnappedOn: matchSnapper = self.pipeSnapper.snapToMap(mousePoint) if matchSnapper.isValid(): valid = False layer = matchSnapper.layer() snapLayerPath = self.getLayerPath(layer) for name in self.ownMainLayers: layerPath = self.generatePath( self.ProjectDirectory, self.NetworkName + "_Pipes.shp") if snapLayerPath == layerPath: valid = True if valid: self.pipeSnapped = matchSnapper self.pipeMarker.setCenter(matchSnapper.point()) self.pipeMarker.show() else: self.pipeMarker.hide() else: self.pipeMarker.hide() self.pipeSnapped = None # # Update rubber band if self.objectSnapped is not None and self.rubberBand is not None: snappedPoint = self.objectSnapped.point() self.newPositionVector = QgsVector( mousePoint.x() - snappedPoint.x(), mousePoint.y() - snappedPoint.y()) self.updateRubberBand() def canvasReleaseEvent(self, event): if self.mouseClicked: if event.button() == 1: mousePoint = self.toMapCoordinates(event.pos()) if (self.pipeSnapped is not None): mousePoint = self.pipeSnapped.point() self.mouseClicked = False if self.objectSnapped is not None: self.moveVertexLink(self.selectedLayer, self.selectedFeature, mousePoint, self.vertexIndex) elif event.button() == 2: if self.objectSnapped is not None: self.deleteVertexLink(self.selectedLayer, self.selectedFeature, self.vertexIndex) elif event.button() == 1: if self.objectSnapped is not None: self.insertVertexLink(self.selectedLayer, self.selectedFeature, self.objectSnapped.point()) self.objectSnapped = None self.pipeSnapped = None self.selectedFeature = None self.selectedLayer = None self.vertexIndex = -1 self.iface.mapCanvas().refresh() # Remove vertex marker and rubber band self.vertexMarker.hide() self.iface.mapCanvas().scene().removeItem(self.rubberBand) self.newVertexMarker.hide() self.pipeMarker.hide()
class MincutConnec(QgsMapTool): canvasClicked = pyqtSignal() def __init__(self, iface, controller): """ Class constructor """ self.iface = iface self.canvas = self.iface.mapCanvas() self.controller = controller # Call superclass constructor and set current action QgsMapTool.__init__(self, self.canvas) self.dragging = False # Vertex marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) # Rubber band self.rubber_band = QgsRubberBand(self.canvas, QGis.Line) self.rubber_band.setColor(color) self.rubber_band.setWidth(1) # Select rectangle self.select_rect = QRect() # TODO: Parametrize self.connec_group = ["Wjoin", "Tap", "Fountain", "Greentap"] #self.snapperManager = SnappingConfigManager(self.iface) self.snapper = QgsMapCanvasSnapper(self.canvas) def activate(self): pass def canvasPressEvent(self, event): #@UnusedVariable self.select_rect.setRect(0, 0, 0, 0) self.rubber_band.reset() def canvasMoveEvent(self, event): """ With left click the digitizing is finished """ if event.buttons() == Qt.LeftButton: if not self.dragging: self.dragging = True self.select_rect.setTopLeft(event.pos()) self.select_rect.setBottomRight(event.pos()) self.set_rubber_band() else: # Hide highlight self.vertex_marker.hide() # Get the click x = event.pos().x() y = event.pos().y() event_point = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers( event_point) # @UnusedVariable # That's the snapped point if result: # Check feature for snap_point in result: element_type = snap_point.layer.name() if element_type in self.connec_group: # Get the point point = QgsPoint(snap_point.snappedVertex) # Add marker self.vertex_marker.setCenter(point) self.vertex_marker.show() break def canvasReleaseEvent(self, event): """ With left click the digitizing is finished """ if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() event_point = QPoint(x, y) # Not dragging, just simple selection if not self.dragging: # Snap to node (retval, result) = self.snapper.snapToBackgroundLayers( event_point) # @UnusedVariable # That's the snapped point if result: # Check feature for snapped_point in result: element_type = snapped_point.layer.name() if element_type in self.connec_group: feat_type = 'connec' else: continue point = QgsPoint( snapped_point.snappedVertex) # @UnusedVariable # layer.removeSelection() # layer.select([result[0].snappedAtGeometry]) #snapped_point.layer.removeSelection() snapped_point.layer.select( [snapped_point.snappedAtGeometry]) else: # Set valid values for rectangle's width and height if self.select_rect.width() == 1: self.select_rect.setLeft(self.select_rect.left() + 1) if self.select_rect.height() == 1: self.select_rect.setBottom(self.select_rect.bottom() + 1) self.set_rubber_band() self.select_multiple_features(self.selected_rectangle) self.dragging = False # Refresh map canvas self.rubber_band.reset() self.refresh_map_canvas() def set_rubber_band(self): # Coordinates transform transform = self.canvas.getCoordinateTransform() # Coordinates ll = transform.toMapCoordinates(self.select_rect.left(), self.select_rect.bottom()) lr = transform.toMapCoordinates(self.select_rect.right(), self.select_rect.bottom()) ul = transform.toMapCoordinates(self.select_rect.left(), self.select_rect.top()) ur = transform.toMapCoordinates(self.select_rect.right(), self.select_rect.top()) # Rubber band self.rubber_band.reset() self.rubber_band.addPoint(ll, False) self.rubber_band.addPoint(lr, False) self.rubber_band.addPoint(ur, False) self.rubber_band.addPoint(ul, False) self.rubber_band.addPoint(ll, True) self.selected_rectangle = QgsRectangle(ll, ur) def select_multiple_features(self, rectangle): if self.connec_group is None: return if QGis.QGIS_VERSION_INT >= 21600: # Selection for all connec group layers for layer_name in self.connec_group: # Get layer by his name layer = self.controller.get_layer_by_layername(layer_name, log_info=True) if layer: self.group_pointers_connec.append(layer) layer.selectByRect(rectangle) else: for layer_name in self.connec_group: self.iface.setActiveLayer(layer) layer.removeSelection() layer.select(rectangle, True)
class AddPipeTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.first_click = False self.rubber_band = QgsRubberBand(self.canvas(), False) self.rubber_band.setColor(QColor(255, 128, 128)) self.rubber_band.setWidth(1) self.rubber_band.setBrushStyle(Qt.Dense4Pattern) self.rubber_band.reset() self.snapper = None self.snapped_feat_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = None self.diameter_dialog = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = False if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) last_ix = self.rubber_band.numberOfVertices() self.rubber_band.movePoint(last_ix - 1, (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt)) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') # Mouse not clicked: snapping to closest vertex match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # Pipe starts from an existing vertex self.snapped_feat_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() # else: # It's a new, isolated pipe self.snapped_vertex = None self.snapped_feat_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): # if not self.mouse_clicked: # return if event.button() == Qt.LeftButton: # Update rubber bands self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) if self.first_click: self.rubber_band.addPoint( (self.snapped_vertex if self.snapped_vertex is not None else self.mouse_pt), True) self.first_click = not self.first_click elif event.button() == Qt.RightButton: # try: pipe_band_geom = self.rubber_band.asGeometry() # No rubber band geometry and feature snapped: pop the context menu if self.rubber_band.size( ) == 0 and self.snapped_feat_id is not None: menu = QMenu() section_action = menu.addAction('Section...') # TODO: softcode diameter_action = menu.addAction( 'Change diameter...') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) pipe_ft = vector_utils.get_feats_by_id(self.params.pipes_vlay, self.snapped_feat_id)[0] if action == section_action: if self.params.dem_rlay is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'No DEM selected. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode else: # Check whether the pipe is all inside the DEM pipe_pts = pipe_ft.geometry().asPolyline() for pt in pipe_pts: if not self.params.dem_rlay.extent().contains(pt): self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Some pipe vertices fall outside of the DEM. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode return # Check whether the start/end nodes have an elevation value (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes( self.params, pipe_ft.geometry()) start_node_elev = start_node_ft.attribute( Junction.field_name_elev) end_node_elev = end_node_ft.attribute( Junction.field_name_elev) if start_node_elev == NULL or end_node_elev == NULL: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Missing elevation value in start or end node attributes. Cannot edit section.', Qgis.Warning, 5) # TODO: softcode return pipe_dialog = PipeSectionDialog( self.iface.mainWindow(), self.iface, self.params, pipe_ft) pipe_dialog.exec_() elif action == diameter_action: old_diam = pipe_ft.attribute(Pipe.field_name_diameter) self.diameter_dialog = DiameterDialog( self.iface.mainWindow(), self.params, old_diam) self.diameter_dialog.exec_() # Exec creates modal dialog new_diameter = self.diameter_dialog.get_diameter() if new_diameter is None: return # Update pipe diameter vector_utils.update_attribute(self.params.pipes_vlay, pipe_ft, Pipe.field_name_diameter, new_diameter) # Check if a valve is present adj_valves = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, pipe_ft, True, True, False) if adj_valves['valves']: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Valves detected on the pipe: need to update their diameters too.', Qgis.Warning, 5) # TODO: softcode return # There's a rubber band: create new pipe if pipe_band_geom is not None: #XPSS ADDED - transform rubber band CRS canvas_crs = self.canvas().mapSettings().destinationCrs() #pipes_vlay = Parameters().pipes_vlay() #print(pipes_vlay) qepanet_crs = self.params.pipes_vlay.sourceCrs() crs_transform = QgsCoordinateTransform(canvas_crs, qepanet_crs, QgsProject.instance()) pipe_band_geom.transform(crs_transform) # Finalize line rubberband_pts = pipe_band_geom.asPolyline()[:-1] if len(rubberband_pts) == 0: self.iface.mapCanvas().refresh() return rubberband_pts = self.remove_duplicated_point(rubberband_pts) if len(rubberband_pts) < 2: self.rubber_band.reset() return # Check whether the pipe points are located on existing nodes junct_nrs = [0] for p in range(1, len(rubberband_pts) - 1): overlapping_nodes = NetworkUtils.find_overlapping_nodes( self.params, rubberband_pts[p]) if overlapping_nodes['junctions'] or overlapping_nodes[ 'reservoirs'] or overlapping_nodes['tanks']: junct_nrs.append(p) junct_nrs.append(len(rubberband_pts) - 1) new_pipes_nr = len(junct_nrs) - 1 new_pipes_fts = [] new_pipes_eids = [] for np in range(new_pipes_nr): pipe_eid = NetworkUtils.find_next_id( self.params.pipes_vlay, Pipe.prefix) # TODO: softcode #demand = float(self.data_dock.txt_pipe_demand.text()) length_units = 'm' #TODO soft code diameter = float(self.data_dock.cbo_pipe_dia.\ currentText()) diameter_units = self.data_dock.cbo_pipe_dia_units.\ currentText() #loss = float(self.data_dock.txt_pipe_loss.text()) #status = self.data_dock.cbo_pipe_status.currentText() material = self.data_dock.cbo_pipe_mtl.currentText() roughness = float(self.data_dock.txt_roughness.text()) #pipe_desc = self.data_dock.txt_pipe_desc.text() #pipe_tag = self.data_dock.cbo_pipe_tag.currentText() num_edu = 1 zone_id = 0 velocity = 0 velocity_units = 'm/s' frictionloss = 0 frictionloss_units = 'm' pipe_ft = LinkHandler.create_new_pipe( self.params, pipe_eid, length_units, diameter, diameter_units, 0, roughness, " ", material, rubberband_pts[junct_nrs[np]:junct_nrs[np + 1] + 1], True, " ", " ", num_edu, zone_id, velocity, velocity_units, frictionloss, frictionloss_units) self.rubber_band.reset() new_pipes_fts.append(pipe_ft) new_pipes_eids.append(pipe_eid) # emitter_coeff_s = self.data_dock.txt_junction_emit_coeff.text() # # if emitter_coeff_s is None or emitter_coeff_s == '': # emitter_coeff = float(0) # else: # emitter_coeff = float(self.data_dock.txt_junction_emit_coeff.text()) # # Description # junction_desc = self.data_dock.txt_junction_desc.text() # # # Tag # junction_tag = self.data_dock.cbo_junction_tag.currentText() zone_end = 0 pressure = 0 pressure_units = self.data_dock.cbo_rpt_units_pressure.currentText( ) # Create start and end node, if they don't exist (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[0].geometry()) new_start_junction = None if not start_junction: new_start_junction = rubberband_pts[0] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_start_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', Qgis.Warning, 5) # TODO: softcode deltaz = float(0) #j_demand = float(self.data_dock.txt_junction_demand.text()) # pattern = self.data_dock.cbo_junction_pattern.itemData( # self.data_dock.cbo_junction_pattern.currentIndex()) # if pattern is not None: # pattern_id = pattern.id # else: # pattern_id = None NodeHandler.create_new_junction(self.params, new_start_junction, junction_eid, elev, 0, deltaz, None, 0, " ", " ", zone_end, pressure, pressure_units) (start_junction, end_junction) = NetworkUtils.find_start_end_nodes( self.params, new_pipes_fts[len(new_pipes_fts) - 1].geometry()) new_end_junction = None if not end_junction: new_end_junction = rubberband_pts[len(rubberband_pts) - 1] junction_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) elev = raster_utils.read_layer_val_from_coord( self.params.dem_rlay, new_end_junction, 1) if elev is None: elev = 0 # If elev is none, and the DEM is selected, it's better to inform the user if self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', Qgis.Warning, 5) # TODO: softcode deltaz = float(0) # pattern = self.data_dock.cbo_junction_pattern.itemData( # self.data_dock.cbo_junction_pattern.currentIndex()) # if pattern is not None: # pattern_id = pattern.id # else: # pattern_id = None NodeHandler.create_new_junction(self.params, new_end_junction, junction_eid, elev, 0, deltaz, None, 0, " ", " ", zone_end, pressure, pressure_units) # If end or start node intersects a pipe, split it if new_start_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[0] and\ pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_start_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_start_junction) if new_end_junction: for pipe_ft in self.params.pipes_vlay.getFeatures(): if pipe_ft.attribute(Pipe.field_name_eid) != new_pipes_eids[-1] and\ pipe_ft.geometry().distance(QgsGeometry.fromPointXY(new_end_junction)) < self.params.tolerance: LinkHandler.split_pipe(self.params, pipe_ft, new_end_junction) self.iface.mapCanvas().refresh() # except Exception as e: # self.rubber_band.reset() # self.iface.messageBar().pushWarning('Cannot add new pipe to ' + self.params.pipes_vlay.name() + ' layer', repr(e)) # traceback.print_exc(file=sys.stdout) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Escape: self.rubber_band.reset() def activate(self): self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() def deactivate(self): # QgsProject.instance().setSnapSettingsForLayer(self.params.junctions_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # # QgsProject.instance().setSnapSettingsForLayer(self.params.reservoirs_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # QgsProject.instance().setSnapSettingsForLayer(self.params.tanks_vlay.id(), # True, # QgsSnapper.SnapToVertex, # QgsTolerance.MapUnits, # 0, # True) # QgsProject.instance().setSnapSettingsForLayer(self.params.pipes_vlay.id(), # True, # QgsSnapper.SnapToSegment, # QgsTolerance.MapUnits, # 0, # True) # self.rubber_band.reset() self.data_dock.btn_add_pipe.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def remove_duplicated_point(self, pts): # This is needed because the rubber band sometimes returns duplicated points purged_pts = [pts[0]] for p in enumerate(list(range(len(pts) - 1)), 1): if pts[p[0]] == pts[p[0] - 1]: continue else: purged_pts.append(pts[p[0]]) return purged_pts def update_snapper(self): layers = { self.params.junctions_vlay: QgsSnappingConfig.Vertex, self.params.reservoirs_vlay: QgsSnappingConfig.Vertex, self.params.tanks_vlay: QgsSnappingConfig.Vertex, self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Needed by Observable def update(self, observable): self.update_snapper()
class GwParentMapTool(QgsMapTool): def __init__(self, icon_path, text, toolbar, action_group, iface, settings, controller, plugin_dir): self.iface = iface self.settings = settings self.controller = controller self.plugin_dir = plugin_dir self.show_help = bool(int(self.settings.value('status/show_help', 1))) self.layer_arc = None self.layer_connec = None self.layer_gully = None self.layer_node = None self.snapper_manager = SnappingConfigManager(self.iface) self.snapper_manager.controller = controller self.canvas = iface.mapCanvas() super().__init__(self.canvas) icon = None if os.path.exists(icon_path): icon = QIcon(icon_path) self.action = None if icon is None: self.action = QAction(text, action_group) else: self.action = QAction(icon, text, action_group) self.action.setObjectName(text) self.action.setCheckable(True) self.action.triggered.connect(self.clicked_event) # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) # Get default cursor # noinspection PyCallingNonCallable self.std_cursor = self.parent().cursor() # Set default vertex marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) # Set default rubber band color_selection = QColor(254, 178, 76, 63) self.rubber_band = QgsRubberBand(self.canvas, 2) self.rubber_band.setColor(color) self.rubber_band.setFillColor(color_selection) self.rubber_band.setWidth(1) self.reset() self.force_active_layer = True toolbar.addAction(self.action) self.setAction(self.action) def clicked_event(self): self.controller.prev_maptool = self.iface.mapCanvas().mapTool() if not (self == self.iface.mapCanvas().mapTool()): self.iface.mapCanvas().setMapTool(self) else: self.iface.mapCanvas().unsetMapTool(self) def deactivate(self): # Uncheck button self.action.setChecked(False) # Restore previous snapping self.snapper_manager.recover_snapping_options() # Enable snapping self.snapper_manager.enable_snapping(True) # Recover cursor self.canvas.setCursor(self.std_cursor) # Remove highlight self.vertex_marker.hide() def canvasMoveEvent(self, event): # Make sure active layer is always 'v_edit_node' cur_layer = self.iface.activeLayer() if cur_layer != self.layer_node and self.force_active_layer: self.iface.setActiveLayer(self.layer_node) # Hide highlight and get coordinates self.vertex_marker.hide() event_point = self.snapper_manager.get_event_point(event) # Snapping result = self.snapper_manager.snap_to_current_layer(event_point) if self.snapper_manager.result_is_valid(): self.snapper_manager.add_marker(result, self.vertex_marker) def recover_previus_maptool(self): if self.controller.prev_maptool: self.iface.mapCanvas().setMapTool(self.controller.prev_maptool) self.controller.prev_maptool = None def remove_vertex(self): """ Remove vertex_marker from canvas""" vertex_items = [i for i in self.iface.mapCanvas().scene().items() if issubclass(type(i), QgsVertexMarker)] for ver in vertex_items: if ver in self.iface.mapCanvas().scene().items(): if self.vertex_marker == ver: self.iface.mapCanvas().scene().removeItem(ver) def set_action_pan(self): """ Set action 'Pan' """ try: self.iface.actionPan().trigger() except Exception: pass def reset_rubber_band(self, geom_type="polygon"): try: if geom_type == "polygon": geom_type = QgsWkbTypes.PolygonGeometry elif geom_type == "line": geom_type = QgsWkbTypes.LineString self.rubber_band.reset(geom_type) except: pass def reset(self): self.reset_rubber_band() self.snapped_feat = None def cancel_map_tool(self): """ Executed if user press right button or escape key """ # Reset rubber band self.reset() # Deactivate map tool self.deactivate() self.set_action_pan() def remove_markers(self): """ Remove previous markers """ vertex_items = [i for i in list(self.canvas.scene().items()) if issubclass(type(i), QgsVertexMarker)] for ver in vertex_items: if ver in list(self.canvas.scene().items()): self.canvas.scene().removeItem(ver) def refresh_map_canvas(self): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint()
class Result(object): def __init__(self,iface,description=None,x=None,y=None,zoom=None,epsg=None): self.iface = iface self.canvas = self.iface.mapCanvas() self.description = unicode(description) self.x = float(x) self.y = float(y) self.zoom = int(zoom) self.epsg = int(epsg) self.marker = QgsVertexMarker(self.canvas) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.setColor(QColor('green')) self._active = False self._visible = False self._xtrans = None self._ytrans = None @property def active(self): return self._active @active.setter def active(self,value): if value == True: self._active = True self.marker.setColor(QColor('red')) self.marker.updateCanvas() else: self._active = False self.marker.setColor(QColor('green')) self.marker.updateCanvas() @property def visible(self): return self._visible @visible.setter def visible(self,value): if value == True: if self.x is not None and self.y is not None: self._visible = True dest_crs = self.canvas.mapRenderer().destinationCrs() src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(self.epsg) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(self.x, self.y) self._xtrans = new_point.x() self._ytrans = new_point.y() self.marker.setCenter(new_point) self.marker.show() else: self._visible = False raise ValueError("Can't show marker without x and y coordinates.") else: self._visible = False self.marker.hide() def unload(self): self.canvas.scene().removeItem(self.marker) self.marker = None def zoomTo(self): if self._xtrans is not None and self._ytrans is not None: r = QgsRectangle(self._xtrans,self._ytrans,self._xtrans,self._ytrans) self.canvas.setExtent(r) self.canvas.zoomScale(self.zoom) self.canvas.refresh() else: raise ValueError("Point does not have x and y coordinates")
class LineMapTool(QgsMapTool): def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.index_action = index_action self.elem_type_type = self.settings.value('insert_values/'+str(index_action)+'_elem_type_type') QgsMapTool.__init__(self, self.canvas) self.setAction(action) # Set rubber band features self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) mFillColor = QColor(255, 0, 0); self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(2) self.reset() # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setIconSize(9) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Snapper self.snapper = QgsMapCanvasSnapper(self.canvas) # Control button state self.mCtrl = False self.started = False # Tracing options self.firstTimeOnSegment = True self.lastPointMustStay = False self.lastPoint = None # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) self.parent().setCursor(self.cursor) def keyPressEvent(self, event): ''' We need to know, if ctrl-key is pressed ''' if event.key() == Qt.Key_Control: self.mCtrl = True def keyReleaseEvent(self, event): ''' Ctrl-key is released ''' if event.key() == Qt.Key_Control: self.mCtrl = False # Remove the last added point when the delete key is pressed if event.key() == Qt.Key_Backspace: self.rubberBand.removeLastPoint() def reset(self): self.start_point = self.end_point = None self.isEmittingPoint = False self.rubberBand.reset(QGis.Line) ''' QgsMapTools inherited event functions ''' def canvasPressEvent(self, event): ''' On left click, we add a point ''' if event.button() == 1: # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() # Declare, that are we going to work self.started = True if layer <> None: x = event.pos().x() y = event.pos().y() selPoint = QPoint(x,y) # Check something snapped (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable # The point we want to have, is either from snapping result if result <> []: point = result[0].snappedVertex # If we snapped something, it's either a vertex if result[0].snappedVertexNr <> -1: self.firstTimeOnSegment = True # Or a point on a segment, so we have to declare, that a point on segment is found else: self.firstTimeOnSegment = False # Or its some point from out in the wild else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.firstTimeOnSegment = True # Bring the rubberband to the cursor i.e. the clicked point self.rubberBand.movePoint(point) # Set a new point to go on with self.appendPoint(point) # Try to remember that this point was on purpose i.e. clicked by the user self.lastPointMustStay = True self.firstTimeOnSegment = True def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Snapping (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> []: point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() # Check tracing if self.started: # Only if the ctrl key is pressed if self.mCtrl == True: # So if we have found a snapping if result <> []: # If it is a vertex, not a point on a segment if result[0].snappedVertexNr <> -1: self.rubberBand.movePoint(point) self.appendPoint(point) self.lastPointMustStay = True # The next point found, may be on a segment self.firstTimeOnSegment = True # We are on a segment else: self.rubberBand.movePoint(point) # If we are on a new segment, we add the point in any case if self.firstTimeOnSegment: self.appendPoint(point) self.lastPointMustStay = True self.firstTimeOnSegment = False # if we are not on a new segment, we have to test, if this point is really needed else: # but only if we have already enough points if self.rubberBand.numberOfVertices() >=3: num_vertexs = self.rubberBand.numberOfVertices() lastRbP = self.rubberBand.getPoint(0, num_vertexs-2) nextToLastRbP = self.rubberBand.getPoint(0, num_vertexs-3) if not self.pointOnLine(lastRbP, nextToLastRbP, QgsPoint(point)): self.appendPoint(point) self.lastPointMustStay = False else: if not self.lastPointMustStay: self.rubberBand.removeLastPoint() self.rubberBand.movePoint(point) else: self.appendPoint(point) self.lastPointMustStay = False self.firstTimeOnSegment = False else: #if nothing specials happens, just update the rubberband to the cursor position point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform (), x, y) self.rubberBand.movePoint(point) else: ''' In "not-tracing" state, just update the rubberband to the cursor position but we have still to snap to act like the "normal" digitize tool ''' if result <> []: point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, event): ''' With right click the digitizing is finished ''' if event.button() == 2: # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() x = event.pos().x() y = event.pos().y() if layer <> None and self.started == True: selPoint = QPoint(x,y) (retval,result) = self.snapper.snapToBackgroundLayers(selPoint) #@UnusedVariable if result <> []: point = result[0].snappedVertex #@UnusedVariable else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) #@UnusedVariable self.sendGeometry() def appendPoint(self, point): ''' Don't add the point if it is identical to the last point we added ''' if not (self.lastPoint == point) : self.rubberBand.addPoint(point) self.lastPoint = QgsPoint(point) else: pass def sendGeometry(self): #layer = self.canvas.currentLayer() layer = self.iface.activeLayer() coords = [] self.rubberBand.removeLastPoint() if QGis.QGIS_VERSION_INT >= 10700: [coords.append(self.rubberBand.getPoint(0, i)) for i in range(self.rubberBand.numberOfVertices())] else: [coords.append(self.rubberBand.getPoint(0,i)) for i in range(1,self.rubberBand.numberOfVertices())] # On the Fly reprojection, not necessary any more, mapToLayerCoordinates is clever enough on its own #layerEPSG = layer.srs().epsg() #projectEPSG = self.canvas.mapRenderer().destinationSrs().epsg() #if layerEPSG != projectEPSG: coords_tmp = coords[:] coords = [] for point in coords_tmp: transformedPoint = self.canvas.mapRenderer().mapToLayerCoordinates( layer, point ); coords.append(transformedPoint) # Filter duplicated points coords_tmp = coords[:] coords = [] lastPt = None for pt in coords_tmp: if (lastPt <> pt) : coords.append(pt) lastPt = pt # Add geometry to feature. g = QgsGeometry().fromPolyline(coords) self.rubberBand.reset(QGis.Line) self.started = False # Write the feature self.createFeature(g) def createFeature(self, geom): # layer = self.canvas.currentLayer() layer = self.iface.activeLayer() provider = layer.dataProvider() f = QgsFeature() if (geom.isGeosValid()): f.setGeometry(geom) else: reply = QMessageBox.question(self.iface.mainWindow(), 'Feature not valid', "The geometry of the feature you just added isn't valid. Do you want to use it anyway?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: f.setGeometry(geom) else: return False # Add attribute fields to feature. fields = layer.pendingFields() try: #API-Break 1.8 vs. 2.0 handling attr = f.initAttributes(len(fields)) #@UnusedVariable for i in range(len(fields)): f.setAttribute(i, provider.defaultValue(i)) except AttributeError: #<=1.8 # Add attributefields to feature. for i in fields: f.addAttribute(i, provider.defaultValue(i)) idx = layer.fieldNameIndex('epa_type') f[idx] = self.elem_type_type # Upload changes layer.startEditing() layer.addFeature(f) # Control PostgreSQL exceptions boolOk = layer.commitChanges() # Update canvas self.canvas.refresh() # Capture edit exception if boolOk: # Spatial query to retrieve last added line, start searchingcandidates cands = layer.getFeatures(QgsFeatureRequest().setFilterRect(f.geometry().boundingBox())) # Iterate on candidates for line_feature in cands: if line_feature.geometry().equals(f.geometry()): # Highlight layer.setSelectedFeatures([line_feature.id()]) # Open form self.iface.openFeatureForm(layer, line_feature) break else: # Delete layer.rollBack() # User error msg = "Error adding PIPE: Typically this occurs if\n the first point is not located in a existing\n node or feature is out of the defined sectors." QMessageBox.information(None, "PostgreSQL error:", msg) def activate(self): self.canvas.setCursor(self.cursor) def deactivate(self): try: self.rubberBand.reset(QGis.Line) except AttributeError: pass
class ExtractRasterValue(ParentMapTool): """ Button 18. User select nodes and assign raster elevation or value """ def __init__(self, iface, settings, action, index_action): """ Class constructor """ # Call ParentMapTool constructor super(ExtractRasterValue, self).__init__(iface, settings, action, index_action) self.dragging = False # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(11) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Rubber band self.rubberBand = QgsRubberBand(self.canvas, True) mFillColor = QColor(100, 0, 0) self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(3) mBorderColor = QColor(254, 58, 29) self.rubberBand.setBorderColor(mBorderColor) # Select rectangle self.selectRect = QRect() # Init self.vectorLayer = None self.rasterLayer = None def reset(self): """ Clear selected features """ layer = self.vectorLayer if layer is not None: layer.removeSelection() # Graphic elements self.rubberBand.reset() def set_config_action(self, action_99): """ Get the config form action""" self.configAction = action_99 """ QgsMapTools inherited event functions """ def canvasMoveEvent(self, event): """ With left click the digitizing is finished """ if self.vectorLayer is None: return if event.buttons() == Qt.LeftButton: if not self.dragging: self.dragging = True self.selectRect.setTopLeft(event.pos()) self.selectRect.setBottomRight(event.pos()) self.set_rubber_band() else: # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer == self.vectorLayer: # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() break def canvasPressEvent(self, event): self.selectRect.setRect(0, 0, 0, 0) self.rubberBand.reset() def canvasReleaseEvent(self, event): """ With left click the digitizing is finished """ if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Node layer layer = self.vectorLayer # Not dragging, just simple selection if not self.dragging: # Snap to node (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.vectorLayer.name()): point = QgsPoint(result[0].snappedVertex) # @UnusedVariable layer.removeSelection() layer.select([result[0].snappedAtGeometry]) # Interpolate values self.raster_interpolate() # Hide highlight self.vertexMarker.hide() else: # Set valid values for rectangle's width and height if self.selectRect.width() == 1: self.selectRect.setLeft(self.selectRect.left() + 1) if self.selectRect.height() == 1: self.selectRect.setBottom(self.selectRect.bottom() + 1) self.set_rubber_band() selectGeom = self.rubberBand.asGeometry() # @UnusedVariable self.select_multiple_features(self.selectRectMapCoord) self.dragging = False # Interpolate values self.raster_interpolate() elif event.button() == Qt.RightButton: # Interpolate values self.raster_interpolate() def activate(self): # Check button self.action().setChecked(True) # Rubber band self.rubberBand.reset() # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Get layers res = self.find_raster_layers() # if res == 0: # self.controller.show_warning("Raster configuration tool not properly configured.") # return # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = ( "Right click to use current selection, select connec points by clicking or dragging (selection box)" ) self.controller.show_info(message, context_name="ui_message") # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_arc) except: self.canvas.setCurrentLayer(self.layer_arc) def deactivate(self): # Check button self.action().setChecked(False) # Rubber band self.rubberBand.reset() # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) def nearestNeighbor(self, thePoint): ident = self.dataProv.identify(thePoint, QgsRaster.IdentifyFormatValue) value = None if ident is not None: # and ident.has_key(choosenBand+1): try: value = float(ident.results()[int(self.band)]) except TypeError: value = None if value == self.noDataValue: return None return value def writeInterpolation(self, f, fieldIdx): thePoint = f.geometry().asPoint() value = self.nearestNeighbor(thePoint) self.vectorLayer.changeAttributeValue(f.id(), fieldIdx, value) def raster_interpolate(self): """ Interpolate features value from raster """ # Interpolate values if self.vectorLayer is None and self.rasterLayer is None: return # Get data provider layer = self.vectorLayer self.dataProv = self.rasterLayer.dataProvider() if self.dataProv.srcNoDataValue(int(self.band)): self.noDataValue = self.dataProv.srcNoDataValue(int(self.band)) else: self.noDataValue = None self.continueProcess = True self.fieldIdx = "" self.fieldIdx = layer.fieldNameIndex(self.fieldName) if self.band == 0: self.controller.show_warning("You must choose a band for the raster layer.") return if self.fieldName == "": self.controller.show_warning("You must choose a field to write values.") return if self.fieldIdx < 0: self.controller.show_warning("Selected field does not exist in feature layer.") return k = 0 c = 0 f = QgsFeature() # Check features selected if layer.selectedFeatureCount() == 0: message = "You have to select at least one feature!" self.controller.show_warning(message, context_name="ui_message") return # Check editable if not layer.isEditable(): layer.startEditing() # Get selected id's ids = self.vectorLayer.selectedFeaturesIds() for fid in ids: k += 1 layer.getFeatures(QgsFeatureRequest(fid)).nextFeature(f) c += 1 self.writeInterpolation(f, self.fieldIdx) QCoreApplication.processEvents() self.controller.show_info( "%u values have been updated in layer %s.%s over %u points using %s raster" % (c, self.vectorLayer.name(), self.fieldName, k, self.table_raster) ) # Check editable if layer.isEditable(): layer.commitChanges() # Refresh map canvas self.rubberBand.reset() self.iface.mapCanvas().refresh() def set_rubber_band(self): # Coordinates transform transform = self.canvas.getCoordinateTransform() # Coordinates ll = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.bottom()) lr = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.bottom()) ul = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.top()) ur = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.top()) # Rubber band self.rubberBand.reset() self.rubberBand.addPoint(ll, False) self.rubberBand.addPoint(lr, False) self.rubberBand.addPoint(ur, False) self.rubberBand.addPoint(ul, False) self.rubberBand.addPoint(ll, True) self.selectRectMapCoord = QgsRectangle(ll, ur) def select_multiple_features(self, selectGeometry): # Default choice behaviour = QgsVectorLayer.SetSelection # Modifiers modifiers = QApplication.keyboardModifiers() if modifiers == Qt.ControlModifier: behaviour = QgsVectorLayer.AddToSelection elif modifiers == Qt.ShiftModifier: behaviour = QgsVectorLayer.RemoveFromSelection if self.vectorLayer is None: return # Change cursor QApplication.setOverrideCursor(Qt.WaitCursor) # Selection self.vectorLayer.selectByRect(selectGeometry, behaviour) # Old cursor QApplication.restoreOverrideCursor() def find_raster_layers(self): # Query database (form data) sql = "SELECT *" sql += " FROM " + self.schema_name + ".config_extract_raster_value" rows = self.controller.get_rows(sql) if not rows: self.controller.show_warning("Any data found in table config_extract_raster_value") self.configAction.activate(QAction.Trigger) return 0 # Layers row = rows[0] self.table_raster = row[1] self.band = row[2] self.table_vector = row[3] self.fieldName = row[4] # Check if we have any layer loaded layers = self.iface.legendInterface().layers() if len(layers) == 0: return # Init layers self.rasterLayer = None self.vectorLayer = None # Iterate over all layers to get the ones specified in 'db' config section for cur_layer in layers: if cur_layer.name() == self.table_raster: self.rasterLayer = cur_layer (uri_schema, uri_table) = self.controller.get_layer_source(cur_layer) # @UnusedVariable if uri_table is not None: if self.table_vector in uri_table: self.vectorLayer = cur_layer # Check config if self.vectorLayer is None or self.rasterLayer is None: self.configAction.activate(QAction.Trigger) # Set snapping if self.vectorLayer <> None: self.snapperManager.snapToLayer(self.vectorLayer) else: self.controller.show_warning("Check vector_layer in config form, layer does not exist or is not defined.") return 0 if self.rasterLayer == None: self.controller.show_warning("Check raster_layer in config form, layer does not exist or is not defined.") return 0 return 1
class ApisMapToolEmitPolygonAndPoint(QgsMapTool, ApisMapToolMixin): mappingFinished = pyqtSignal(QgsGeometry, QgsGeometry, QgsCoordinateReferenceSystem) def __init__(self, canvas): QgsMapTool.__init__(self, canvas) self.canvas = canvas self.rubberBand = None self.tempRubberBand = None self.vertexMarker = None self.capturedPoints = [] self.derivedPoint = None self.capturing = False self.setCursor(Qt.CrossCursor) def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if not self.capturing: self.startCapturing() self.addVertex(event.pos()) elif event.button() == Qt.RightButton: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def canvasMoveEvent(self, event): if self.tempRubberBand != None and self.capturing: mapPt = self.transformCoordinates(event.pos()) self.tempRubberBand.movePoint(mapPt) def keyPressEvent(self, event): if event.key() == Qt.Key_Backspace or event.key() == Qt.Key_Delete: self.removeLastVertex() event.ignore() if event.key() == Qt.Key_Escape: self.stopCapturing() self.clearScene() if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter: point = self.getDerivedPoint() polygon = self.getCapturedPolygon() self.stopCapturing() if point != None and polygon != None: pointGeom = self.getPointGeometry(point) polygonGeom = self.getPolygonGeometry(polygon) if pointGeom != None and polygonGeom != None: self.mappingFinished.emit(pointGeom, polygonGeom, self.canvas.mapSettings().destinationCrs()) else: self.clearScene() else: self.clearScene() def startCapturing(self): self.clearScene() self.rubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.rubberBand.setWidth(2) self.rubberBand.setFillColor(QColor(220, 0, 0, 120)) self.rubberBand.setBorderColor(QColor(220, 0, 0)) self.rubberBand.setLineStyle(Qt.DotLine) self.rubberBand.show() self.tempRubberBand = QgsRubberBand(self.canvas, QGis.Polygon) self.tempRubberBand.setWidth(2) self.tempRubberBand.setFillColor(QColor(0, 0, 0, 0)) self.tempRubberBand.setBorderColor(QColor(220, 0, 0)) self.tempRubberBand.setLineStyle(Qt.DotLine) self.tempRubberBand.show() self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setIconType(1) self.vertexMarker.setColor(QColor(220, 0, 0)) self.vertexMarker.setIconSize(16) self.vertexMarker.setPenWidth(3) self.vertexMarker.show() self.capturing = True def clearScene(self): if self.vertexMarker: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None def stopCapturing(self): if self.vertexMarker and self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.vertexMarker) self.vertexMarker = None if self.rubberBand and self.rubberBand.numberOfVertices() < 3: self.canvas.scene().removeItem(self.rubberBand) self.rubberBand = None if self.tempRubberBand: self.canvas.scene().removeItem(self.tempRubberBand) self.tempRubberBand = None self.capturing = False self.capturedPoints = [] self.derivedPoint = None self.canvas.refresh() def addVertex(self, canvasPoint): mapPt = self.transformCoordinates(canvasPoint) self.rubberBand.addPoint(mapPt) self.capturedPoints.append(mapPt) bandSize = self.rubberBand.numberOfVertices() if bandSize > 2: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() self.tempRubberBand.reset(QGis.Polygon) firstPoint = self.rubberBand.getPoint(0, 0) self.tempRubberBand.addPoint(firstPoint) self.tempRubberBand.movePoint(mapPt) self.tempRubberBand.addPoint(mapPt) def removeLastVertex(self): if not self.capturing: return bandSize = self.rubberBand.numberOfVertices() tempBandSize = self.tempRubberBand.numberOfVertices() numPoints = len(self.capturedPoints) if bandSize < 1 or numPoints < 1: return self.rubberBand.removePoint(-1) if bandSize > 1: if tempBandSize > 1: point = self.rubberBand.getPoint(0, bandSize - 2) self.tempRubberBand.movePoint(tempBandSize - 2, point) else: self.tempRubberBand.reset(QGis.Polygon) bandSize = self.rubberBand.numberOfVertices() if bandSize < 3: self.vertexMarker.hide() else: rubGeom = self.rubberBand.asGeometry() cpGeom = rubGeom.centroid() if not rubGeom.contains(cpGeom): cpGeom = rubGeom.pointOnSurface() #nearestCp = rubGeom.nearestPoint(cpGeom) self.vertexMarker.setCenter(cpGeom.asPoint()) self.derivedPoint = cpGeom.asPoint() self.vertexMarker.show() del self.capturedPoints[-1] def getCapturedPolygon(self): polygon = self.capturedPoints if len(polygon) < 3: return None else: return polygon def getDerivedPoint(self): point = self.derivedPoint if point == None: return None else: return point def getPointGeometry(self, geom): p = QgsGeometry.fromPoint(geom) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None def getPolygonGeometry(self, geom): p = QgsGeometry.fromPolygon([geom]) if p.isGeosValid() and not p.isGeosEmpty(): return p else: return None
class ZoomToPostcode: """QGIS Plugin Implementation.""" METADATA_URL = "http://qgis.locationcentre.co.uk/ZoomToPostcode_medata.xml" POSTCODE_URL = "http://qgis.locationcentre.co.uk/UK_Postcodes.zip" def __init__(self, iface): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # Save reference to the QGIS interface self.iface = iface self.canvas = iface.mapCanvas() # initialize plugin directory self.plugin_dir = path.dirname(__file__) # initialize locale locale = QSettings().value('locale/userLocale')[0:2] locale_path = path.join( self.plugin_dir, 'i18n', 'ZoomToPostcode_{}.qm'.format(locale)) if path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) # Create the dialog (after translation) and keep reference self.dlg = ZoomToPostcodeDialog() # Create licence dlg self.licence_dlg = LicenceDialog() # Create various class references self.marker = None self.completer = None self.previous_searches = [] # Declare instance attributes self.actions = [] self.menu = self.tr(u'&Zoom To Postcode') self.toolbar = self.iface.addToolBar(u'ZoomToPostcode') self.toolbar.setObjectName(u'ZoomToPostcode') # noinspection PyMethodMayBeStatic def tr(self, message): """Get the translation for a string using Qt translation API. We implement this ourselves since we do not inherit QObject. :param message: String for translation. :type message: str, QString :returns: Translated version of message. :rtype: QString """ # noinspection PyTypeChecker,PyArgumentList,PyCallByClass return QCoreApplication.translate('ZoomToPostcode', message) def add_action( self, icon_path, text, callback, enabled_flag=True, add_to_menu=True, add_to_toolbar=True, status_tip=None, whats_this=None, parent=None): """Add a toolbar icon to the toolbar.""" icon = QIcon(icon_path) action = QAction(icon, text, parent) action.triggered.connect(callback) action.setEnabled(enabled_flag) if status_tip is not None: action.setStatusTip(status_tip) if whats_this is not None: action.setWhatsThis(whats_this) if add_to_toolbar: self.toolbar.addAction(action) if add_to_menu: self.iface.addPluginToMenu( self.menu, action) self.actions.append(action) return action def initGui(self): """Create the menu entries and toolbar icons inside the QGIS GUI.""" icon_path = ':/plugins/zoom_to_postcode/icon.png' self.add_action( icon_path, text=self.tr(u'Zoom to postcode'), callback=self.run, parent=self.iface.mainWindow()) # Configure toolbar widget self.toolbar = self.iface.addToolBar("Zoom To Postcode Toolbar") self.toolbar.setObjectName("Zoom To Postcode Toolbar") self.toolbar_search = QLineEdit() self.toolbar_search.setMaximumWidth(100) self.toolbar_search.setAlignment(Qt.AlignLeft) self.toolbar_search.setPlaceholderText("Enter postcode...") self.toolbar.addWidget(self.toolbar_search) self.toolbar_search.returnPressed.connect(self.check_pkl) self.search_btn = QAction(QIcon(":/plugins/zoomtopostcode/zoomicon.png"), "Search", self.iface.mainWindow()) self.search_btn.triggered.connect(self.check_pkl) self.toolbar.addActions([self.search_btn]) # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/zoomtopostcode/zoomicon.png"), u"Zoom to Postcode", self.iface.mainWindow()) self.action.triggered.connect(self.toolbar.show) self.licence = QAction(u"OS Licence", self.iface.mainWindow()) self.licence.triggered.connect(self.licence_dlg.show) # Add toolbar button and menu item self.iface.addPluginToMenu(u"&Zoom to Postcode", self.action) self.iface.addPluginToMenu(u"&Zoom to Postcode", self.licence) def unload(self): """Removes the plugin menu item and icon from QGIS GUI.""" for action in self.actions: self.iface.removePluginMenu( self.tr(u'&Zoom To Postcode'), action) self.iface.removeToolBarIcon(action) # remove the toolbar del self.toolbar def run(self): """Show the toolbar dialog.""" self.dlg.show() def search_completer(self): self.completer = QCompleter(self.previous_searches, self.iface.mainWindow()) self.completer.setCompletionMode(QCompleter.PopupCompletion) self.completer.setCaseSensitivity(Qt.CaseInsensitive) def check_crs(self): """Check if a transformation needs to take place. :return: None """ srs = self.canvas.mapSettings().destinationCrs() current_crs = srs.authid() return current_crs def transform(self, cor): """Transforms point from british nation grid to map crs. :param cor: Coords :return: Point """ srs = self.canvas.mapSettings().destinationCrs() crs_src = QgsCoordinateReferenceSystem(27700) crs_dest = QgsCoordinateReferenceSystem(srs) xform = QgsCoordinateTransform(crs_src, crs_dest, QgsProject.instance()) x, y = cor t_point = xform.transform(float(x), float(y)) return t_point def check_pkl(self): """Check the Pickle postcode dir exists. :return: None """ checkpkl = path.isdir(path.join(self.plugin_dir, 'UK_Postcodes')) if checkpkl: xml_path = path.join(self.plugin_dir, r'UK_Postcodes/metadata.xml') check_xml = path.isfile(xml_path) if check_xml: check_currency = self.check_pcode_date(xml_path) if check_currency: self.postcode_dict() else: self.update_pcode_option() else: self.update_pcode_option() else: msg = "Postcode files must be downloaded to use this plugin, do you wish to continue?" goahead = QMessageBox.question(self.iface.mainWindow(), "Download Message", msg, QMessageBox.Yes, QMessageBox.No) if goahead == QMessageBox.Yes: self.download_pkl() else: pass def check_pcode_date(self, xml_path): """Parses metadata xml to check currency of pcodes. :param xml_path: Full path to xml file. :return: True for up-to-date postcodes, False requires download to update """ try: http = urllib3.PoolManager() r = http.request('GET', self.METADATA_URL, retries=3) xml_as_string = ET.fromstring(r.data.decode('utf-8')) tree_web = ET.ElementTree(xml_as_string) root_web = tree_web.getroot() current_version = "" for child in root_web: if child.tag == "pcode_date": current_version = child.text tree_plugin = ET.parse(xml_path) root_plugin = tree_plugin.getroot() last_update = "" for child in root_plugin: if child.tag == "pcode_date": last_update = child.text last_up_datetime = datetime.strptime(last_update, '%Y-%m-%d') curr_ver_datetime = datetime.strptime(current_version, '%Y-%m-%d') if last_up_datetime.date() >= curr_ver_datetime.date(): return True else: return False except urllib3.exceptions.MaxRetryError: QMessageBox.information(self.iface.mainWindow(), "HTTP Error", "Unable to download file") def update_pcode_option(self): """Provide option to update postcodes. :return: None """ msg = "Updated postcode files are available, do you wish to download?" goahead = QMessageBox.question(self.iface.mainWindow(), "Download Message", msg, QMessageBox.Yes, QMessageBox.No) if goahead == QMessageBox.Yes: self.download_pkl() if goahead == QMessageBox.No: self.postcode_dict() def download_pkl(self): """Download the Pickle postcode file to the plugin dir. :return: None """ pcode_path = path.join(path.dirname(__file__), 'UK_Postcodes') if not path.exists(pcode_path): makedirs(pcode_path) try: http = urllib3.PoolManager() response = http.request('GET', self.POSTCODE_URL, preload_content=False, retries=3) content_length = response.headers['Content-Length'] total_size = int(content_length) downloaded = 0 CHUNK = 256 * 10240 dlbar = QProgressBar() dlbar.setMinimum(0) dlbar.setMaximum(total_size) zip_temp = tempfile.NamedTemporaryFile(mode='w+b', suffix='.zip', delete=False) zip_temp_n = zip_temp.name zip_temp.seek(0) with open(zip_temp_n, 'wb') as fp: while True: dlbar.show() chunk = response.read(CHUNK) downloaded += len(chunk) dlbar.setValue(downloaded) if not chunk: break fp.write(chunk) response.release_conn() pcode_zip = ZipFile(zip_temp) pcode_zip.extractall(pcode_path) zip_temp.close() self.check_pkl() except urllib3.exceptions.MaxRetryError: QMessageBox.information(self.iface.mainWindow(), "HTTP Error", "Unable to download file") def postcode_dict(self): """Create dictionary of postcodes from correct Pickle file. :return: None """ try: input_pcode = self.toolbar_search.text().replace(' ', '') if input_pcode[1].isdigit(): find_pkl = str(r"UK_Postcodes/" + input_pcode[:1] + ".pkl") else: find_pkl = str(r"UK_Postcodes/" + input_pcode[:2] + ".pkl") pklfile = open(path.join(path.dirname(__file__), find_pkl), 'rb') pcode_dict = pickle.load(pklfile) pklfile.close() if input_pcode.upper() not in self.previous_searches: self.previous_searches.append(input_pcode.upper()) self.search_completer() self.toolbar_search.setCompleter(self.completer) self.zoomto(pcode_dict) except (KeyError, IOError): QMessageBox.information(self.iface.mainWindow(), "Invalid postcode", "The postcode you entered was not found") except IndexError: pass def zoomto(self, pcode_dict): """Find the coordinates for postcode in the dictionary. :param pcode_dict: :return: None """ current_crs = self.check_crs() input_pc = self.toolbar_search.text() input_pc_fmt = str(input_pc).replace(' ', '').upper() coords = pcode_dict[input_pc_fmt] x, y = coords if current_crs != "EPSG:27700": cor = (x, y) point = self.transform(cor) self.update_canvas(point) else: point = (x, y) self.update_canvas(point) def update_canvas(self, point): """Update the canvas and add vertex marker. :param point: :return: None """ x, y = point scale = 120 rect = QgsRectangle(float(x) - scale, float(y) - scale, float(x) + scale, float(y) + scale) self.canvas.setExtent(rect) self.marker = QgsVertexMarker(self.canvas) self.marker.setIconSize(15) self.marker.setPenWidth(2) self.marker.setCenter(QgsPointXY(int(x), int(y))) self.canvas.refresh() self.canvas.extentsChanged.connect(self.remove_marker) def remove_marker(self): """Remove vertex marker. :return: None """ self.marker.hide() self.canvas.scene().removeItem(self.marker) self.canvas.extentsChanged.disconnect(self.remove_marker)
# coding: utf-8 from PyQt4.QtGui import QColor from qgis.utils import iface from qgis.core import QgsPoint from qgis.gui import QgsVertexMarker canvas = iface.mapCanvas() m = QgsVertexMarker(canvas) m.setCenter(QgsPoint(0, 0)) m.setColor(QColor(0, 0, 255)) m.setIconSize(7) m.setIconType(QgsVertexMarker.ICON_BOX) # See the enum IconType from http://www.qgis.org/api/classQgsVertexMarker.html m.setPenWidth(3) m.hide() m.show() # Remove the element # canvas.scene().removeItem(m)
class mapillary_cursor(): def transformToWGS84(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs( ) # get current crs crsSrc = crcMappaCorrente crsDest = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def transformToCurrentSRS(self, pPoint): # transformation from the current SRS to WGS84 crcMappaCorrente = self.iface.mapCanvas().mapSettings().destinationCrs( ) # get current crs crsDest = crcMappaCorrente crsSrc = QgsCoordinateReferenceSystem(4326) # WGS 84 xform = QgsCoordinateTransform(crsSrc, crsDest, QgsProject.instance()) return xform.transform(pPoint) # forward transformation: src -> dest def __init__(self, parentInstance): self.parentInstance = parentInstance self.iface = parentInstance.iface self.mapCanvas = self.iface.mapCanvas() self.lineOfSight = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.sightDirection = QgsRubberBand(self.mapCanvas, QgsWkbTypes.LineGeometry) self.pointOfView = QgsVertexMarker(self.mapCanvas) self.cursor = QgsVertexMarker(self.mapCanvas) self.sightDirection.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setColor(QColor(CURSOR_COLOR)) self.pointOfView.setColor(QColor(CURSOR_COLOR)) self.cursor.setColor(QColor(CURSOR_COLOR)) self.lineOfSight.setWidth(2) self.sightDirection.setWidth(1) self.sightDirection.setLineStyle(Qt.DashLine) self.pointOfView.setIconType(QgsRubberBand.ICON_CIRCLE) self.cursor.setIconType(QgsRubberBand.ICON_CIRCLE) self.pointOfView.setIconSize(20) self.cursor.setIconSize(20) self.cursor.setPenWidth(2) self.pointOfView.setPenWidth(2) self.samples_datasource = '' self.delete() #self.update_ds(self.parentInstance.sample_settings.settings['sample_source']) def getSamplesLayer(self, samples_datasource): if samples_datasource != 'memory': if not os.path.exists(samples_datasource): self.create_datasource_from_template(samples_datasource) samples_lyr = QgsVectorLayer(samples_datasource, SAMPLES_LAYER_NAME, 'ogr') else: samples_lyr = QgsVectorLayer("Point?crs=epsg:4326&index=yes", SAMPLES_LAYER_NAME, 'memory') self.checkForTemplateFields(samples_lyr) return samples_lyr def getFieldFromDefinition(self, field_type): type_pack = field_type[1].split("|") if field_type[2]: comment = field_type[2] else: comment = field_type[0] return QgsField(name=field_type[0], type=int(type_pack[0]), len=int(type_pack[1]), prec=int(type_pack[2]), comment=comment) def checkForTemplateFields(self, layer): layerFieldNamesList = [] for field in layer.fields().toList(): layerFieldNamesList.append(field.name()) for fieldDef in FIELDS_TEMPLATE: if not fieldDef[0] in layerFieldNamesList: layer.startEditing() layer.addAttribute(self.getFieldFromDefinition(fieldDef)) layer.commitChanges() def update_ds(self, ds): if self.samples_datasource != ds: self.samples_datasource = ds self.samplesLayer = self.getSamplesLayer(ds) self.samplesLayer.triggerRepaint() self.samplesLayer.loadNamedStyle( os.path.join(os.path.dirname(__file__), "res", "mapillary_samples.qml")) self.samplesLayer.featureAdded.connect(self.newAddedFeat) def create_datasource_from_template(self, datasource): fieldSet = QgsFields() for fieldDef in FIELDS_TEMPLATE: fieldSet.append(self.getFieldFromDefinition(fieldDef)) writer = QgsVectorFileWriter(datasource, 'UTF-8', fieldSet, QgsWkbTypes.Point, QgsCoordinateReferenceSystem(4326), "ESRI Shapefile") if writer.hasError(): print("error", writer.errorMessage()) del writer def draw(self, pointOfView_coords, orig_pointOfView_coords, cursor_coords, endOfSight_coords): if pointOfView_coords[0]: self.cursor.show() self.pointOfView.show() self.lineOfSight.reset() self.sightDirection.reset() pointOfView = self.transformToCurrentSRS( QgsPointXY(pointOfView_coords[1], pointOfView_coords[0])) cursor = self.transformToCurrentSRS( QgsPointXY(cursor_coords[1], cursor_coords[0])) endOfSight = self.transformToCurrentSRS( QgsPointXY(endOfSight_coords[1], endOfSight_coords[0])) self.pointOfView.setCenter(pointOfView) self.cursor.setCenter(cursor) self.lineOfSight.addPoint(pointOfView) self.lineOfSight.addPoint(cursor) self.sightDirection.addPoint(pointOfView) self.sightDirection.addPoint(endOfSight) self.cursor.updatePosition() else: self.delete() def delete(self): self.cursor.hide() self.pointOfView.hide() self.lineOfSight.reset() self.sightDirection.reset() def addSampleLayerToCanvas(self): if not QgsProject.instance().mapLayer(self.samplesLayer.id()): QgsProject.instance().addMapLayer(self.samplesLayer) self.restoreMarkers() def sample(self, type, id, key, sample_coords, img_coords=None): self.samplesLayer.startEditing() samplePoint = QgsPointXY(sample_coords[1], sample_coords[0]) #sampleDevicePoint = self.iface.mapCanvas().getCoordinateTransform().transform(samplePoint.x(),samplePoint.y()) self.addSampleLayerToCanvas() sampleFeat = QgsFeature(self.samplesLayer.fields()) sampleFeat['type'] = type sampleFeat['id'] = id sampleFeat['key'] = key if img_coords: sampleFeat['img_coords'] = img_coords sampleFeat.setGeometry(QgsGeometry.fromPointXY(samplePoint)) #self.samplesLayer.dataProvider().addFeatures([sampleFeat]) print('', self.samplesLayer.addFeature(sampleFeat)) self.samplesLayer.commitChanges() def newAddedFeat(self, featId): if featId < 0: return self.samplesLayer.triggerRepaint() if self.parentInstance.sample_settings.settings['auto_open_form']: newFeat = self.samplesLayer.getFeature(featId) self.parentInstance.samples_form.open(newFeat) def getSamplesList(self): samples = [] id = 1 for feat in self.samplesLayer.getFeatures(): samples.append({ "id": id, "latLon": { 'lat': feat.geometry().asPoint().y(), 'lon': feat.geometry().asPoint().x(), } }) def restoreTags(self, key): exp = QgsExpression('"type" = \'tag\' and "key" = \'%s\'' % key) tags = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings[ 'categories'][str(feat['cat'])] else: color = '#ffffff' tags.append({ 'id': str(feat['id']), 'key': str(feat['key']), 'note': str(feat['note']), 'cat': str(feat['cat']), 'color': color, 'geometry': json.loads(feat['img_coords']) }) return tags def editSample(self, type, key, id): exp = QgsExpression( '"type" = \'%s\' and "key" = \'%s\' and "id" = \'%s\'' % (type, key, id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): self.parentInstance.samples_form.open(feat) def moveMarker(self, key, id, sample_coords): exp = QgsExpression( '"type" = \'marker\' and "key" = \'%s\' and "id" = \'%s\'' % (key, id)) for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): samplePoint = QgsPointXY(sample_coords[1], sample_coords[0]) self.samplesLayer.startEditing() self.samplesLayer.changeGeometry( feat.id(), QgsGeometry.fromPointXY(samplePoint)) self.samplesLayer.commitChanges() self.samplesLayer.triggerRepaint() def restoreMarkers(self): if self.parentInstance.sample_settings.settings[ 'sample_source'] != 'memory': exp = QgsExpression('"type" = \'marker\'') markersDef = [] for feat in self.samplesLayer.getFeatures(QgsFeatureRequest(exp)): if feat['cat']: color = self.parentInstance.sample_settings.settings[ 'categories'][str(feat['cat'])] else: color = '#ffffff' markersDef.append({ 'key': str(feat['key']), 'id': str(feat['id']), 'color': color, 'loc': { 'lon': feat.geometry().asPoint().x(), 'lat': feat.geometry().asPoint().y() } }) self.parentInstance.viewer.addMarkers(markersDef)
class AddJunctionTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.snapper = None self.snapped_feat_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = False if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') if not self.mouse_clicked: # Mouse not clicked: snapping to closest vertex # (retval, result) = self.snapper.snapMapPoint(self.toMapCoordinates(event.pos())) match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # if len(result) > 0: self.snapped_feat_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() # It's a vertex on an existing pipe # self.snapped_feat_id = result[0].snappedAtGeometry # # snapped_vertex = result[0].snappedVertex # self.snapped_vertex_nr = result[0].snappedVertexNr # self.snapped_vertex = QgsPoint(snapped_vertex.x(), snapped_vertex.y()) self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: # It's a new, isolated vertex self.snapped_feat_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # Find first available ID for Junctions node_eid = NetworkUtils.find_next_id( self.params.junctions_vlay, Junction.prefix) # TODO: softcode elev = 0 if self.elev is None and self.params.dem_rlay is not None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Elevation value not available: element elevation set to 0.', QgsMessageBar.WARNING, 5) # TODO: softcode else: elev = self.elev j_demand = float(self.data_dock.txt_junction_demand.text()) deltaz = float(self.data_dock.txt_junction_deltaz.text()) pattern = self.data_dock.cbo_junction_pattern.itemData( self.data_dock.cbo_junction_pattern.currentIndex()) if pattern is not None: pattern_id = pattern.id else: pattern_id = None emitter_coeff_s = self.data_dock.txt_junction_emit_coeff.text() if emitter_coeff_s is None or emitter_coeff_s == '': emitter_coeff = float(0) else: emitter_coeff = float( self.data_dock.txt_junction_emit_coeff.text()) junction_desc = self.data_dock.txt_junction_desc.text() junction_tag = self.data_dock.cbo_junction_tag.currentText() # No links snapped: create a new stand-alone node if self.snapped_feat_id is None: NodeHandler.create_new_junction(self.params, self.mouse_pt, node_eid, elev, j_demand, deltaz, pattern_id, emitter_coeff, junction_desc, junction_tag) # A link has been snapped else: # Get the snapped pipe request = QgsFeatureRequest().setFilterFid( self.snapped_feat_id) feats = list(self.params.pipes_vlay.getFeatures(request)) if len(feats) > 0: snapped_pipe = QgsFeature(feats[0]) snapped_ft_pts = QgsGeometry.asPolyline( snapped_pipe.geometry()) # The new junction is on the start or end node of the pipe if NetworkUtils.points_overlap(QgsGeometry.fromPoint(self.snapped_vertex), QgsGeometry.fromPoint(snapped_ft_pts[0]), self.params.tolerance) or\ NetworkUtils.points_overlap(QgsGeometry.fromPoint(self.snapped_vertex), QgsGeometry.fromPoint(snapped_ft_pts[len(snapped_ft_pts) - 1]), self.params.tolerance): NodeHandler.create_new_junction( self.params, self.snapped_vertex, node_eid, self.elev, j_demand, deltaz, pattern_id, emitter_coeff, junction_desc, junction_tag) else: # Split the pipe (start_node_ft, end_node_ft) = NetworkUtils.find_start_end_nodes( self.params, snapped_pipe.geometry()) if start_node_ft is None or end_node_ft is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pipe is missing the start or end nodes. Cannot add a new junction along the pipe.', QgsMessageBar.WARNING, 5) # TODO: softcode return # Check that the snapped point on line is distant enough from start/end nodes if start_node_ft.geometry().distance(QgsGeometry.fromPoint(self.snapped_vertex)) > self.params.min_dist and\ end_node_ft.geometry().distance(QgsGeometry.fromPoint(self.snapped_vertex)) > self.params.min_dist: NodeHandler.create_new_junction( self.params, self.snapped_vertex, node_eid, self.elev, j_demand, deltaz, pattern_id, emitter_coeff, junction_desc, junction_tag) LinkHandler.split_pipe(self.params, snapped_pipe, self.snapped_vertex) else: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The new junction is too close to either the pipe end or start nodes. Cannot add a new junction along the pipe.', QgsMessageBar.WARNING, 5) # TODO: softcode def activate(self): # Snapping self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() def deactivate(self): if self.params.pipes_vlay is not None: QgsProject.instance().setSnapSettingsForLayer( self.params.pipes_vlay.id(), True, QgsSnapper.SnapToSegment, 0, self.params.snap_tolerance, True) self.data_dock.btn_add_junction.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def update_snapper(self): layers = { self.params.junctions_vlay: QgsPointLocator.Vertex, self.params.pipes_vlay: QgsPointLocator.All } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Needed by Observable def update(self, observable): self.update_snapper()
class AddValveTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.snapper = None self.snapped_pipe_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = -1 self.diameter_dialog = None def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = True if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') if not self.mouse_clicked: # Mouse not clicked: snapping to closest vertex # (retval, result) = self.snapper.snapMapPoint(self.toMapCoordinates(event.pos())) # if len(result) > 0: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # It's a vertex on an existing pipe # self.snapped_pipe_id = result[0].snappedAtGeometry # snapped_vertex = result[0].snappedVertex # self.snapped_vertex_nr = result[0].snappedVertexNr # self.snapped_vertex = QgsPoint(snapped_vertex.x(), snapped_vertex.y()) self.snapped_pipe_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: # It's a new, isolated vertex self.snapped_pipe_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # No pipe snapped: notify user if self.snapped_pipe_id is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'You need to snap the cursor to a pipe to add a valve.', QgsMessageBar.INFO, 5) # A pipe has been snapped else: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: # Check whether the pipe has a start and an end node (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry()) if not start_node or not end_node: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pipe is missing the start or end nodes.', QgsMessageBar.WARNING, 5) # TODO: softcode return # Find endnode closest to valve position dist_1 = start_node.geometry().distance( QgsGeometry.fromPoint(self.snapped_vertex)) dist_2 = end_node.geometry().distance( QgsGeometry.fromPoint(self.snapped_vertex)) # Get the attributes of the closest junction (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry(), False, True, True) if dist_1 < dist_2: closest_junction_ft = start_node else: closest_junction_ft = end_node # Create the valve diameter = features[0].attribute(Pipe.field_name_diameter) # diameter = self.data_dock.txt_valve_diameter.text() minor_loss = self.data_dock.txt_valve_minor_loss.text() selected_type = self.data_dock.cbo_valve_type.itemData( self.data_dock.cbo_valve_type.currentIndex()) if selected_type != Valve.type_gpv: setting = self.data_dock.txt_valve_setting.text() else: valve_curve = self.data_dock.cbo_valve_curve.itemData( self.data_dock.cbo_valve_curve.currentIndex()) if valve_curve is not None: setting = valve_curve.id else: setting = None # Pump status valve_status = self.data_dock.cbo_valve_status.itemData( self.data_dock.cbo_valve_status.currentIndex()) # Valve description valve_desc = self.data_dock.txt_valve_desc.text() # Valve tag valve_tag = self.data_dock.cbo_valve_tag.currentText() try: LinkHandler.create_new_pumpvalve( self.params, self.data_dock, features[0], closest_junction_ft, self.snapped_vertex, self.params.valves_vlay, { Valve.field_name_diameter: diameter, Valve.field_name_minor_loss: minor_loss, Valve.field_name_setting: setting, Valve.field_name_type: selected_type, Valve.field_name_status: valve_status, Valve.field_name_description: valve_desc, Valve.field_name_tag: valve_tag }) except PumpValveCreationException as ex: self.iface.messageBar().pushMessage( Parameters.plug_in_name, ex.message, QgsMessageBar.INFO, 5) elif event.button() == Qt.RightButton: self.mouse_clicked = False # Check whether it clicked on a valve vertex if len( NetworkUtils.find_adjacent_links( self.params, self.snapped_vertex)['valves']) == 0: return menu = QMenu() diameter_action = menu.addAction( 'Change diameter...') # TODO: softcode invert_action = menu.addAction( 'Flip orientation') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) request = QgsFeatureRequest().setFilterFid(self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: adj_links = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, features[0], True, True, False) for valve_ft in adj_links['valves']: if action == diameter_action: old_diam = valve_ft.attribute( Valve.field_name_diameter) self.diameter_dialog = DiameterDialog( self.iface.mainWindow(), self.params, old_diam) self.diameter_dialog.exec_( ) # Exec creates modal dialog new_diameter = self.diameter_dialog.get_diameter() if new_diameter is None: return # Update valve diameter vector_utils.update_attribute( self.params.valves_vlay, valve_ft, Valve.field_name_diameter, new_diameter) # Modify pipes diameters adj_pipes_fts = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.valves_vlay, valve_ft, False, True, True) if adj_pipes_fts: for adj_pipe_ft in adj_pipes_fts['pipes']: vector_utils.update_attribute( self.params.pipes_vlay, adj_pipe_ft, Pipe.field_name_diameter, new_diameter) self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'Diameters of pipes adjacent to valve updated.', QgsMessageBar.INFO, 5) # TODO: softcode elif action == invert_action: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: adj_links = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, features[0], True, True, False) for adj_link in adj_links['valves']: adj_link_pts = adj_link.geometry().asPolyline() for adj_link_pt in adj_link_pts: if NetworkUtils.points_overlap( adj_link_pt, self.snapped_vertex, self.params.tolerance): geom = adj_link.geometry() if geom.wkbType( ) == QGis.WKBMultiLineString: nodes = geom.asMultiPolyline() for line in nodes: line.reverse() newgeom = QgsGeometry.fromMultiPolyline( nodes) self.params.valves_vlay.changeGeometry( adj_link.id(), newgeom) if geom.wkbType( ) == QGis.WKBLineString: nodes = geom.asPolyline() nodes.reverse() newgeom = QgsGeometry.fromPolyline( nodes) self.params.valves_vlay.changeGeometry( adj_link.id(), newgeom) self.iface.mapCanvas().refresh() break def activate(self): snap_layer_pipes = NetworkUtils.set_up_snap_layer( self.params.pipes_vlay, None, QgsSnapper.SnapToVertexAndSegment) self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): QgsProject.instance().setSnapSettingsForLayer( self.params.pipes_vlay.id(), True, QgsSnapper.SnapToSegment, QgsTolerance.MapUnits, 0, True) self.data_dock.btn_add_valve.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def update_snapper(self): layers = {self.params.pipes_vlay: QgsPointLocator.All} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) # Needed by Observable def update(self, observable): self.update_snapper()
class BeoordelingstoolDockWidget(QtGui.QDockWidget, FORM_CLASS): closingPlugin = pyqtSignal() def __init__(self, parent=None, manhole_layer=None, pipe_layer=None, measuring_point_layer=None): """Constructor.""" super(BeoordelingstoolDockWidget, self).__init__(parent) # 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.add_herstelmaatregelen() self.tabWidget.currentChanged.connect(self.tab_changed) self.manholes = manhole_layer self.pipes = pipe_layer self.measuring_points = measuring_point_layer self.manholes.selectionChanged.connect(self.get_selected_manhole) self.pipes.selectionChanged.connect(self.get_selected_pipe) self.measuring_points.selectionChanged.connect( self.get_selected_measuring_point) # Enable the 'Select Feature` tool by default. iface.actionSelect().trigger() # General tab self.set_project_properties() self.pushbutton_upload_voortgang_json.clicked.connect( self.show_login_dialog_voortgang) self.pushbutton_upload_zip_json.clicked.connect( self.show_login_dialog_final) # Manholes tab self.selected_manhole_id = 0 self.pushbutton_save_attribute_manholes.clicked.connect( self.save_beoordeling_putten) # Pipes tab self.selected_pipe_id = 0 self.pushbutton_save_attribute_pipes.clicked.connect( self.save_beoordeling_leidingen) self.pushbutton_pipe_to_measuring_point.clicked.connect( self.show_measuring_point) # Measuring points tab self.selected_measuring_point_id = 0 self.pushbutton_save_attribute_measuring_points.clicked.connect( self.save_beoordeling_measuring_points) self.pushbutton_measuring_points_previous.clicked.connect( self.show_previous_measuring_point) self.pushbutton_measuring_point_to_pipe.clicked.connect(self.show_pipe) self.pushbutton_measuring_points_next.clicked.connect( self.show_next_measuring_point) self.selected_measuring_points_ids = [] self.measure_point_marker = QgsVertexMarker(iface.mapCanvas()) self.measure_point_marker.setColor(Qt.blue) self.disable_next_measuring_point_button() self.disable_previous_measuring_point_button() def closeEvent(self, event): self.measure_point_marker.hide() self.closingPlugin.emit() event.accept() def add_herstelmaatregelen(self): """Add the herstelmaatregelen to the comboboxes""" self.field_combobox_manholes.addItems(HERSTELMAATREGELEN) self.field_combobox_pipes.addItems(HERSTELMAATREGELEN) self.field_combobox_measuring_points.addItems(HERSTELMAATREGELEN) def tab_changed(self): """Change the active layer upon selecting another tab.""" if self.tabWidget.currentIndex() == 1: # Set manholes as active layer manholes_layer = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_MANHOLES)[0] iface.setActiveLayer(manholes_layer) elif self.tabWidget.currentIndex() == 2: # Set pipes as active layer pipes_layer = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_PIPES)[0] iface.setActiveLayer(pipes_layer) elif self.tabWidget.currentIndex() == 3: # Set measuring_points as active layer measuring_points_layer = QgsMapLayerRegistry.instance( ).mapLayersByName(SHP_NAME_MEASURING_POINTS)[0] iface.setActiveLayer(measuring_points_layer) def set_project_properties(self): """ Set the project name on the General tab of the dockwidget. The name of the project name property of the review.json in the same folder as the layer is used as the project name. """ # Check if the manholes, pipes and measuring_points layers exist if self.manholes and self.pipes and self.measuring_points: # Get project name from the json saved in the same folder as the "manholes" layer layer_dir = get_layer_dir(self.manholes) json_path = os.path.join(layer_dir, JSON_NAME) try: data = json.load(open(json_path)) if data[JSON_KEY_PROJ][JSON_KEY_NAME]: self.label_project_name.setText( data[JSON_KEY_PROJ][JSON_KEY_NAME]) else: iface.messageBar().pushMessage("Warning", "No project name defined.", level=QgsMessageBar.WARNING, duration=0) if data[JSON_KEY_PROJ][JSON_KEY_URL]: self.textedit_project_url.setText( "<a href=google.com>{}</a>".format( data[JSON_KEY_PROJ][JSON_KEY_URL])) # .clicked(QDesktopServices.openUrl(QUrl(data[JSON_KEY_PROJ][JSON_KEY_URL], QUrl.TolerantMode))) # self.label_project_url.setText("<a href={}>{}</a>".format(data[JSON_KEY_PROJ][JSON_KEY_URL])) else: iface.messageBar().pushMessage("Warning", "No project url defined.", level=QgsMessageBar.WARNING, duration=0) except: # TODO: bare except. Also fails when there's a json if it is without project url. iface.messageBar().pushMessage( "Error", "No {} found.".format(JSON_NAME), level=QgsMessageBar.CRITICAL, duration=0) def show_login_dialog_voortgang(self): """ Show the login dialog. If the user data typed in the login dialog is correct, a json is created from the shapefiles and uploaded to the server. """ # Check if the manholes, pipes and measuring_points layers exist self.login_dialog = BeoordelingstoolLoginDialog() self.login_dialog.show() self.login_dialog.output.connect(self.upload_voortgang) def show_login_dialog_final(self): """ Show the login dialog. If the user data typed in the login dialog is correct, a json is created from the shapefiles and uploaded to the server. A zip is also created from these shapefiles and json and uploaded to the server. """ self.login_dialog = BeoordelingstoolLoginDialog() self.login_dialog.show() self.login_dialog.output.connect(self.upload_final) def upload_voortgang(self, user_data): """Upload the voortgang (json).""" print "voortgang" review_json = self.convert_shps_to_json() # upload json to server (get url from json) save_json_to_server(review_json, user_data) iface.messageBar().pushMessage("Info", "JSON uploaded.", level=QgsMessageBar.INFO, duration=0) def upload_final(self, user_data): """Upload the final version (json + zip with shapefiles and qmls).""" review_json = self.convert_shps_to_json() # Upload JSON save_json_to_server(review_json, user_data) # Upload zip # Check if the manholes, pipes and measuring_points layers exist manholes_layerList = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_MANHOLES) pipes_layerList = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_PIPES) measuring_points_layerList = QgsMapLayerRegistry.instance( ).mapLayersByName(SHP_NAME_MEASURING_POINTS) if manholes_layerList and pipes_layerList and measuring_points_layerList: # Get project name from the json saved in the same folder as the "manholes" layer layer_dir = get_layer_dir(manholes_layerList[0]) temp_dir = tempfile.mkdtemp(prefix="beoordelingstool") project_name = review_json[JSON_KEY_PROJ][JSON_KEY_NAME] zip_url = review_json[JSON_KEY_PROJ][JSON_KEY_URL] create_zip(project_name, layer_dir, temp_dir) save_zip_to_server(project_name, temp_dir, zip_url, user_data) iface.messageBar().pushMessage("Info", "JSON and ZIP uploaded.", level=QgsMessageBar.INFO, duration=0) def convert_shps_to_json(self): """ Convert the manholes, pipes and measuring points shapefiles to a json. Args: (dict) user_data: A dict containing the username and password. Returns: (dict) json_: A dict containing the information about the project and the shapefiles. """ # Check if the manholes, pipes and measuring_points layers exist manholes_layerList = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_MANHOLES) pipes_layerList = QgsMapLayerRegistry.instance().mapLayersByName( SHP_NAME_PIPES) measuring_points_layerList = QgsMapLayerRegistry.instance( ).mapLayersByName(SHP_NAME_MEASURING_POINTS) if manholes_layerList and pipes_layerList and measuring_points_layerList: # Get directory to save json in layer_dir = get_layer_dir(manholes_layerList[0]) json_path = os.path.join(layer_dir, JSON_NAME) # Pipes pipes = create_pipes_json(pipes_layerList[0], measuring_points_layerList[0]) # Manholes manholes = create_manholes_json(manholes_layerList[0]) json_ = {} json_project = {} if json_path: data = json.load(open(json_path)) if data[JSON_KEY_PROJ][JSON_KEY_NAME]: json_project[JSON_KEY_NAME] = data[JSON_KEY_PROJ][ JSON_KEY_NAME] else: iface.messageBar().pushMessage( "Error", "No project name found.", level=QgsMessageBar.CRITICAL, duration=0) return json_ if data[JSON_KEY_PROJ][JSON_KEY_URL]: json_project[JSON_KEY_URL] = data[JSON_KEY_PROJ][ JSON_KEY_URL] else: iface.messageBar().pushMessage( "Error", "No project url found.", level=QgsMessageBar.CRITICAL, duration=0) return json_ if data[JSON_KEY_PROJ][JSON_KEY_SLUG]: json_project[JSON_KEY_SLUG] = data[JSON_KEY_PROJ][ JSON_KEY_SLUG] else: iface.messageBar().pushMessage( "Error", "No project slug found.", level=QgsMessageBar.CRITICAL, duration=0) return json_ else: iface.messageBar().pushMessage("Error", "No json found.", level=QgsMessageBar.CRITICAL, duration=0) return json_[JSON_KEY_PROJ] = json_project json_["pipes"] = pipes json_["manholes"] = manholes with open("{}".format(json_path), 'w') as outfile: json.dump(json_, outfile, indent=2) return json_ else: iface.messageBar().pushMessage( "Warning", "You don't have a manholes, pipes and measuring_points layer.", level=QgsMessageBar.WARNING, duration=0) json_ = {} return json_ def get_selected_manhole(self): layer = iface.activeLayer() for f in layer.selectedFeatures(): self.field_combobox_manholes.setCurrentIndex(self.field_combobox_manholes.findText(str(f["Herstelmaa"]))) \ if self.field_combobox_manholes.findText(str(f["Herstelmaa"])) else self.field_combobox_manholes.setCurrentIndex(0) self.value_plaintextedit_manholes.setPlainText( f["Opmerking"] if type(f["Opmerking"] ) is not QPyNullVariant else "") for index, field in enumerate(MANHOLE_FIELDS): value = f[field] if type( f[field]) is not QPyNullVariant else "" self.tablewidget_manholes.setItem(0, index, QTableWidgetItem(value)) self.mark_feature(f) self.selected_manhole_id = f.id() def save_beoordeling_putten(self): """Save herstelmaatregel and opmerking in the shapefile.""" layer = iface.activeLayer() manhole_id = self.selected_manhole_id if manhole_id is None: return herstelmaatregel = str(self.field_combobox_manholes.currentText()) opmerking = str(self.value_plaintextedit_manholes.toPlainText()) layer.startEditing() layer.changeAttributeValue(manhole_id, 55, herstelmaatregel) # Herstelmaatregel layer.changeAttributeValue(manhole_id, 56, opmerking) # Opmerking layer.commitChanges() layer.triggerRepaint() iface.messageBar().pushMessage("Info", "Manhole saved", level=QgsMessageBar.INFO, duration=5) def show_pipe(self): """Show the pipe to which a measuring point belongs.""" measure_point = self.measuring_points.getFeatures( QgsFeatureRequest().setFilterFid( self.selected_measuring_point_id)).next() pipe_id = int(measure_point.attribute('PIPE_ID')) self.pipes.setSelectedFeatures([pipe_id]) def get_selected_pipe(self): selected_pipes = self.pipes.selectedFeatures() if len(selected_pipes) > 1: # Set the pipe selection to the first pipe. This will cause a # selectionChanged-signal which will call this method again. self.pipes.setSelectedFeatures([selected_pipes[0].id()]) return if len(selected_pipes) == 1: # always show the first pipe self.display_pipe(selected_pipes[0].id()) def display_pipe(self, pipe_id): """Show the pipe_id attributes on the pipe-tab""" pipe = self.pipes.getFeatures(QgsFeatureRequest(pipe_id)).next() hestelmaatregel = self.field_combobox_pipes.findText( str(pipe["Herstelmaa"])) if hestelmaatregel: self.field_combobox_pipes.setCurrentIndex(hestelmaatregel) else: self.field_combobox_pipes.setCurrentIndex(0) opmerking = pipe['Opmerking'] if opmerking: self.value_plaintextedit_pipes.setPlainText(opmerking) else: self.value_plaintextedit_pipes.setPlainText("") for index, field in enumerate(PIPE_FIELDS): value = pipe[field] if type( pipe[field]) is not QPyNullVariant else "" self.tablewidget_pipes.setItem(0, index, QTableWidgetItem(value)) iface.setActiveLayer(self.pipes) self.pipes.triggerRepaint() # Go to the pipe tab self.tabWidget.setCurrentIndex(2) self.selected_pipe_id = pipe.id() def save_beoordeling_leidingen(self): """Save herstelmaatregel and opmerking in the shapefile.""" layer = iface.activeLayer() pipe_id = self.selected_pipe_id if pipe_id is None: return herstelmaatregel = str(self.field_combobox_pipes.currentText()) opmerking = str(self.value_plaintextedit_pipes.toPlainText()) layer.startEditing() layer.changeAttributeValue(pipe_id, 68, herstelmaatregel) # Herstelmaatregel layer.changeAttributeValue(pipe_id, 69, opmerking) # Opmerking layer.commitChanges() layer.triggerRepaint() iface.messageBar().pushMessage("Info", "Pipe saved", level=QgsMessageBar.INFO, duration=5) def show_measuring_point(self): """Show the measuring point that belongs to a certain pipe.""" if not self.measuring_points: iface.messageBar().pushMessage( "Error", "There is no measuring points layer.", level=QgsMessageBar.CRITICAL, duration=0) return expr = QgsExpression("\"PIPE_ID\" IS '{}'".format( self.selected_pipe_id)) measuring_points = self.measuring_points.getFeatures( QgsFeatureRequest(expr)) ids = [measuring_point.id() for measuring_point in measuring_points] if len(ids) == 0: iface.messageBar().pushMessage( "Warning", "There are no measuring points connected to this pipe.", level=QgsMessageBar.WARNING, duration=0) return # Setting the selected measure points causes the measure_points onchange to trigger # which calls get_selected_measuring_point() to display the measure point. self.measuring_points.setSelectedFeatures(ids) iface.setActiveLayer(self.measuring_points) self.measuring_points.triggerRepaint() # Go to measuring points tab self.tabWidget.setCurrentIndex(3) def _display_measuring_point_attributes(self, feature): self.mark_feature(feature) # Trigger trigger = feature.attribute('Trigger') or '' self.value_measpoint_trigger.setText(trigger) # Herstelmaatregel: hmr = 0 or self.field_combobox_measuring_points.findText( str(feature["Herstelmaa"])) self.field_combobox_measuring_points.setCurrentIndex(hmr) # Opmerking: self.value_plaintextedit_measuring_points.setPlainText( str(feature["Opmerking"]) if type(feature["Opmerking"] ) is not QPyNullVariant else "") # Pipe ID self.tablewidget_measuring_points.setItem( 0, 0, QTableWidgetItem(feature["PIPE_ID"])) # Other attributes: field_names = set(field.name() for field in feature.fields()) for idx, code in enumerate(list('ABCDEFGIJKLMNO'), start=1): if code == 'A': # Translate feature A into its description text_to_display = RIBX_CODE_DESCRIPTION_MAPPING.get( feature["A"], feature["A"]) self.tablewidget_measuring_points.setItem( 0, idx, QTableWidgetItem(text_to_display)) continue input_text = '' if code in field_names: input_text = str(feature[code]) self.tablewidget_measuring_points.setItem( 0, idx, QTableWidgetItem(QTableWidgetItem(input_text, 1))) def mark_feature(self, feature): """Set the marker to feature""" self.measure_point_marker.setCenter(feature.geometry().asPoint()) self.measure_point_marker.setVisible(True) def get_selected_measuring_point(self): layer = self.measuring_points measuring_point_ids = [ feature.id() for feature in layer.selectedFeatures() ] if len(measuring_point_ids) > 0: # We have some measuring points in our selection # Display the first one: self.selected_measuring_points_ids = measuring_point_ids feature = layer.selectedFeatures()[0] self.selected_measuring_point_id = feature.id() self._display_measuring_point_attributes(feature) self.display_measuring_points_count() def show_previous_measuring_point(self): """Show the next measuring point.""" current_index = self.selected_measuring_points_ids.index( self.selected_measuring_point_id) self.selected_measuring_point_id = self.selected_measuring_points_ids[ current_index - 1] new_measuring_points = self.measuring_points.getFeatures( QgsFeatureRequest().setFilterFid(self.selected_measuring_point_id)) new_measuring_point = list(new_measuring_points)[0] self._display_measuring_point_attributes(new_measuring_point) self.display_measuring_points_count() def show_next_measuring_point(self): """Show the next measuring point.""" current_index = self.selected_measuring_points_ids.index( self.selected_measuring_point_id) self.selected_measuring_point_id = self.selected_measuring_points_ids[ current_index + 1] new_measuring_points = self.measuring_points.getFeatures( QgsFeatureRequest().setFilterFid(self.selected_measuring_point_id)) new_measuring_point = list(new_measuring_points)[0] self._display_measuring_point_attributes(new_measuring_point) self.display_measuring_points_count() def display_measuring_points_count(self): current = self.selected_measuring_points_ids.index( self.selected_measuring_point_id) total = len(self.selected_measuring_points_ids) self.Nr_measuring_points.setText('{} / {}'.format(current + 1, total)) if current == 0: self.disable_previous_measuring_point_button() else: self.enable_previous_measuring_point_button() if current + 1 >= total: self.disable_next_measuring_point_button() elif current + 1 < total: self.enable_next_measuring_point_button() def disable_previous_measuring_point_button(self): self.pushbutton_measuring_points_previous.setDisabled(True) def enable_previous_measuring_point_button(self): self.pushbutton_measuring_points_previous.setEnabled(True) def disable_next_measuring_point_button(self): self.pushbutton_measuring_points_next.setDisabled(True) def enable_next_measuring_point_button(self): self.pushbutton_measuring_points_next.setEnabled(True) def save_beoordeling_measuring_points(self): """Save herstelmaatregel and opmerking in the shapefile.""" layer = iface.activeLayer() measuring_point_id = self.selected_measuring_point_id if measuring_point_id is None: return herstelmaatregel = str( self.field_combobox_measuring_points.currentText()) opmerking = str( self.value_plaintextedit_measuring_points.toPlainText()) layer.startEditing() layer.changeAttributeValue(measuring_point_id, 16, herstelmaatregel) # Herstelmaatregel layer.changeAttributeValue(measuring_point_id, 17, opmerking) # Opmerking layer.commitChanges() layer.triggerRepaint() iface.messageBar().pushMessage("Info", "Measuring point saved", level=QgsMessageBar.INFO, duration=5)
class gazetteerSearch: def __init__(self, iface): self.dock = None self.results = [] # Save reference to the QGIS interface self.iface = iface self.iface.newProjectCreated.connect(self._hideMarker) self.iface.projectRead.connect(self._hideMarker) self.canvas = self.iface.mapCanvas() self.marker = QgsVertexMarker(self.iface.mapCanvas()) self.marker.setIconSize(20) self.marker.setPenWidth(3) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.hide() # Create the dialog and keep reference self.widget = gazetteerSearchDialog() self.widget.runSearch.connect(self.runSearch) self.widget.ui.clearButton.pressed.connect(self.clearResults) self.widget.zoomRequested.connect(self.zoomTo) # initialize plugin directory self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path( ) + "/python/plugins/gazetteersearch" # initialize locale localePath = "" locale = QSettings().value("locale/userLocale").toString()[0:2] if QFileInfo(self.plugin_dir).exists(): localePath = self.plugin_dir + "/i18n/gazetteersearch_" + locale + ".qm" if QFileInfo(localePath).exists(): self.translator = QTranslator() self.translator.load(localePath) if qVersion() > '4.3.3': QCoreApplication.installTranslator(self.translator) def initGui(self): # Create action that will start plugin configuration self.action = QAction(QIcon(":/plugins/gazetteersearch/icon.png"), \ u"Gazetteer Search", self.iface.mainWindow()) # connect the action to the run method self.action.triggered.connect(self.run) # Add toolbar button and menu item self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu(u"&Gazetteer Search", self.action) def unload(self): # Remove the plugin menu item and icon self.iface.removePluginMenu(u"&Gazetteer Search", self.action) self.iface.removeToolBarIcon(self.action) self.iface.mapCanvas().scene().removeItem(self.marker) self.marker = None def _hideMarker(self): self.marker.hide() # run method that performs all the real work def run(self): if not self.dock: self.dock = QDockWidget("Gazetteer Search", self.iface.mainWindow()) self.dock.setWidget(self.widget) self.iface.addDockWidget(Qt.RightDockWidgetArea, self.dock) self.gazetteers = common.getGazetteers() for gazetter in self.gazetteers.iterkeys(): self.widget.addGazetter(gazetter) if len(self.gazetteers) == 1: self.widget.hideGazetteers() else: self.dock.show() def runSearch(self, searchString, selectedGazetteer): gazetteer_config = self.gazetteers[str(selectedGazetteer)] gazetteer = self.getGazetteerModule(gazetteer_config) url = common.prepareURL(gazetteer.url, gazetteer.params, searchString) data = common.search(url) try: self.results = list(gazetteer.parseRequestResults(data)) except ValueError: self.results = [] if len(self.results) == 0: self.widget.addError('No results found for "%s"' % searchString) for res in self.results: self.widget.addResult(res.description) def clearResults(self): self.widget.clearResults() self.marker.hide() def getGazetteerModule(self, config): gazetteer_module = config['gazetteer'] imported_gazetteer = import_module('gazetteers.%s' % gazetteer_module) return imported_gazetteer def zoomTo(self, name): for res in self.results: if unicode(res.description) == unicode(name): dest_crs = self.canvas.mapRenderer().destinationCrs() src_crs = QgsCoordinateReferenceSystem() src_crs.createFromEpsg(res.epsg) transform = QgsCoordinateTransform(src_crs, dest_crs) new_point = transform.transform(res.x, res.y) x = new_point.x() y = new_point.y() self.canvas.setExtent(QgsRectangle(x, y, x, y)) self.canvas.zoomScale(res.zoom) self.canvas.refresh() self.marker.setCenter(new_point) self.marker.show() return
class ProfileDockWidget(QDockWidget): """ DockWidget class to display the profile """ closeSignal = pyqtSignal() def __init__(self, iface, geometry, mntButton=False, zerosButton=False): """ Constructor :param iface: interface :param width: dock widget geometry """ QDockWidget.__init__(self) self.setWindowTitle(QCoreApplication.translate("VDLTools", "Profile Tool")) self.__iface = iface self.__geom = geometry self.__canvas = self.__iface.mapCanvas() self.__types = ['PDF', 'PNG'] # ], 'SVG', 'PS'] self.__libs = [] if Qwt5_loaded: self.__lib = 'Qwt5' self.__libs.append('Qwt5') if matplotlib_loaded: self.__libs.append('Matplotlib') elif matplotlib_loaded: self.__lib = 'Matplotlib' self.__libs.append('Matplotlib') else: self.__lib = None self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No graph lib available (qwt5 or matplotlib)"), level=QgsMessageBar.CRITICAL, duration=0) self.__doTracking = False self.__vline = None self.__profiles = None self.__numLines = None self.__mntPoints = None self.__marker = None self.__tabmouseevent = None if self.__geom is not None: self.setGeometry(self.__geom) self.__contentWidget = QWidget() self.setWidget(self.__contentWidget) self.__boxLayout = QHBoxLayout() self.__contentWidget.setLayout(self.__boxLayout) self.__plotFrame = QFrame() self.__frameLayout = QHBoxLayout() self.__plotFrame.setLayout(self.__frameLayout) self.__printLayout = QHBoxLayout() self.__printLayout.addWidget(self.__plotFrame) self.__legendLayout = QVBoxLayout() self.__printLayout.addLayout(self.__legendLayout) self.__printWdg = QWidget() self.__printWdg.setLayout(self.__printLayout) self.__plotWdg = None self.__changePlotWidget() size = QSize(150, 20) self.__boxLayout.addWidget(self.__printWdg) self.__vertLayout = QVBoxLayout() self.__libCombo = QComboBox() self.__libCombo.setFixedSize(size) self.__libCombo.addItems(self.__libs) self.__vertLayout.addWidget(self.__libCombo) self.__libCombo.currentIndexChanged.connect(self.__setLib) if mntButton: self.__displayMnt = False self.__mntButton = QPushButton(QCoreApplication.translate("VDLTools", "Display MNT")) self.__mntButton.setFixedSize(size) self.__mntButton.clicked.connect(self.__mnt) self.__vertLayout.addWidget(self.__mntButton) if zerosButton: self.__displayZeros = False self.__zerosButton = QPushButton(QCoreApplication.translate("VDLTools", "Display Zeros")) self.__zerosButton.setFixedSize(size) self.__zerosButton.clicked.connect(self.__zeros) self.__vertLayout.addWidget(self.__zerosButton) else: self.__displayZeros = True self.__maxLabel = QLabel("y max") self.__maxLabel.setFixedSize(size) self.__vertLayout.addWidget(self.__maxLabel) self.__maxSpin = QSpinBox() self.__maxSpin.setFixedSize(size) self.__maxSpin.setRange(-10000, 10000) self.__maxSpin.valueChanged.connect(self.__reScalePlot) self.__vertLayout.addWidget(self.__maxSpin) self.__vertLayout.insertSpacing(10, 20) self.__minLabel = QLabel("y min") self.__minLabel.setFixedSize(size) self.__vertLayout.addWidget(self.__minLabel) self.__minSpin = QSpinBox() self.__minSpin.setFixedSize(size) self.__minSpin.setRange(-10000, 10000) self.__minSpin.valueChanged.connect(self.__reScalePlot) self.__vertLayout.addWidget(self.__minSpin) self.__vertLayout.insertSpacing(10, 40) self.__typeCombo = QComboBox() self.__typeCombo.setFixedSize(size) self.__typeCombo.addItems(self.__types) self.__vertLayout.addWidget(self.__typeCombo) self.__saveButton = QPushButton(QCoreApplication.translate("VDLTools", "Save")) self.__saveButton.setFixedSize(size) self.__saveButton.clicked.connect(self.__save) self.__vertLayout.addWidget(self.__saveButton) self.__boxLayout.addLayout(self.__vertLayout) self.__maxSpin.setEnabled(False) self.__minSpin.setEnabled(False) self.__colors = [] for cn in QColor.colorNames(): qc = QColor(cn) val = qc.red() + qc.green() + qc.blue() if 0 < val < 450: self.__colors.append(cn) def mntButton(self): """ To get the mnt button instance :return: mnt button instance """ return self.__mntButton def zerosButton(self): """ To get the zeros button instance :return: zeros button instance """ return self.__zerosButton def displayMnt(self): """ To get if we want to display mnt :return: true or false """ return self.__displayMnt def __mnt(self): """ To toggle mnt display choice """ if self.__displayMnt: self.__displayMnt = False self.__mntButton.setText(QCoreApplication.translate("VDLTools", "Display MNT")) else: self.__displayMnt = True self.__mntButton.setText(QCoreApplication.translate("VDLTools", "Remove MNT")) def __zeros(self): """ To toggle if we want to display zero elevations or not """ if self.__displayZeros: self.__displayZeros = False self.__zerosButton.setText(QCoreApplication.translate("VDLTools", "Display Zeros")) else: self.__displayZeros = True self.__zerosButton.setText(QCoreApplication.translate("VDLTools", "Remove Zeros")) def __changePlotWidget(self): """ When plot widget is change (qwt <-> matplotlib) """ self.__activateMouseTracking(False) while self.__frameLayout.count(): child = self.__frameLayout.takeAt(0) child.widget().deleteLater() self.__plotWdg = None if self.__lib == 'Qwt5': self.__plotWdg = QwtPlot(self.__plotFrame) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(10) sizePolicy.setVerticalStretch(0) self.__plotWdg.setSizePolicy(sizePolicy) self.__plotWdg.setAutoFillBackground(False) # Decoration self.__plotWdg.setCanvasBackground(Qt.white) self.__plotWdg.plotLayout().setAlignCanvasToScales(False) self.__plotWdg.plotLayout().setSpacing(100) self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.xBottom) self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.yLeft) title = QwtText(QCoreApplication.translate("VDLTools", "Distance [m]")) title.setFont(QFont("Helvetica", 10)) self.__plotWdg.setAxisTitle(QwtPlot.xBottom, title) title.setText(QCoreApplication.translate("VDLTools", "Elevation [m]")) title.setFont(QFont("Helvetica", 10)) self.__plotWdg.setAxisTitle(QwtPlot.yLeft, title) self.__zoomer = QwtPlotZoomer(QwtPlot.xBottom, QwtPlot.yLeft, QwtPicker.DragSelection, QwtPicker.AlwaysOff, self.__plotWdg.canvas()) self.__zoomer.setRubberBandPen(QPen(Qt.blue)) grid = QwtPlotGrid() grid.setPen(QPen(QColor('grey'), 0, Qt.DotLine)) grid.attach(self.__plotWdg) self.__frameLayout.addWidget(self.__plotWdg) elif self.__lib == 'Matplotlib': fig = Figure((1.0, 1.0), linewidth=0.0, subplotpars=SubplotParams(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)) font = {'family': 'arial', 'weight': 'normal', 'size': 12} rc('font', **font) rect = fig.patch rect.set_facecolor((0.9, 0.9, 0.9)) self.__axes = fig.add_axes((0.07, 0.16, 0.92, 0.82)) self.__axes.set_xbound(0, 1000) self.__axes.set_ybound(0, 1000) self.__manageMatplotlibAxe(self.__axes) self.__plotWdg = FigureCanvasQTAgg(fig) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) self.__plotWdg.setSizePolicy(sizePolicy) self.__frameLayout.addWidget(self.__plotWdg) def setProfiles(self, profiles, numLines): """ To set the profiles :param profiles: profiles : positions with elevations (for line and points) :param numLines: number of selected connected lines """ self.__numLines = numLines self.__profiles = profiles if self.__lib == 'Matplotlib': self.__prepare_points() def __getLinearPoints(self): """ To extract the linear points of the profile """ profileLen = 0 self.__profiles[0]['l'] = profileLen for i in range(0, len(self.__profiles)-1): x1 = float(self.__profiles[i]['x']) y1 = float(self.__profiles[i]['y']) x2 = float(self.__profiles[i+1]['x']) y2 = float(self.__profiles[i+1]['y']) profileLen += sqrt(((x2-x1)*(x2-x1)) + ((y2-y1)*(y2-y1))) self.__profiles[i+1]['l'] = profileLen def __getMnt(self, settings): """ To get the MN data for the profile :param settings: settings containing MN url """ if settings is None or settings.mntUrl is None or settings.mntUrl == "None": url = 'https://map.lausanne.ch/prod/wsgi/profile.json' elif settings.mntUrl == "": return else: url = settings.mntUrl names = ['MNT', 'MNS', 'Rocher (approx.)'] data = "layers=MNT%2CMNS%2CRocher%20(approx.)&geom=%7B%22type%22%3A%22LineString%22%2C%22coordinates%22%3A%5B" pos = 0 for i in range(len(self.__profiles)): if pos > 0: data += "%2C" pos += 1 data += "%5B" + str(self.__profiles[i]['x']) + "%2C" + str(self.__profiles[i]['y']) + "%5D" data += "%5D%7D&nbPoints=" + str(int(self.__profiles[len(self.__profiles)-1]['l']+1)) try: response = requests.post(url, data=data) j = response.text j_obj = json.loads(j) profile = j_obj['profile'] self.__mntPoints = [] self.__mntPoints.append(names) mnt_l = [] mnt_z = [] for p in range(len(names)): z = [] mnt_z.append(z) for pt in profile: mnt_l.append(float(pt['dist'])) values = pt['values'] for p in range(len(names)): if names[p] in values: mnt_z[p].append(float(values[names[p]])) else: mnt_z[p].append(None) self.__mntPoints.append(mnt_l) self.__mntPoints.append(mnt_z) except HTTPError as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "HTTP Error"), QCoreApplication.translate("VDLTools", "status error") + "[" + str(e.code) + "] : " + e.reason, level=QgsMessageBar.CRITICAL, duration=0) except URLError as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "URL Error"), e.reason, level=QgsMessageBar.CRITICAL, duration=0) except ValueError as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No MNT values here"), level=QgsMessageBar.CRITICAL, duration=0) def attachCurves(self, names, settings, usedMnts): """ To attach the curves for the layers to the profile :param names: layers names """ if (self.__profiles is None) or (self.__profiles == 0): return self.__getLinearPoints() if usedMnts is not None and (usedMnts[0] or usedMnts[1] or usedMnts[2]): self.__getMnt(settings) c = 0 if self.__mntPoints is not None: for p in range(len(self.__mntPoints[0])): if usedMnts[p]: legend = QLabel("<font color='" + self.__colors[c] + "'>" + self.__mntPoints[0][p] + "</font>") self.__legendLayout.addWidget(legend) if self.__lib == 'Qwt5': xx = [list(g) for k, g in itertools.groupby(self.__mntPoints[1], lambda x: x is None) if not k] yy = [list(g) for k, g in itertools.groupby(self.__mntPoints[2][p], lambda x: x is None) if not k] for j in range(len(xx)): curve = QwtPlotCurve(self.__mntPoints[0][p]) curve.setData(xx[j], yy[j]) curve.setPen(QPen(QColor(self.__colors[c]), 3)) curve.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': qcol = QColor(self.__colors[c]) self.__plotWdg.figure.get_axes()[0].plot(self.__mntPoints[1], self.__mntPoints[2][p], gid=self.__mntPoints[0][p], linewidth=3) tmp = self.__plotWdg.figure.get_axes()[0].get_lines() for t in range(len(tmp)): if self.__mntPoints[0][p] == tmp[t].get_gid(): tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0), old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0))) self.__plotWdg.draw() break c += 1 if 'z' in self.__profiles[0]: for i in range(len(self.__profiles[0]['z'])): if i < self.__numLines: v = 0 else: v = i - self.__numLines + 1 name = names[v] xx = [] yy = [] for prof in self.__profiles: xx.append(prof['l']) yy.append(prof['z'][i]) for j in range(len(yy)): if yy[j] is None: xx[j] = None if i == 0 or i > (self.__numLines-1): legend = QLabel("<font color='" + self.__colors[c] + "'>" + name + "</font>") self.__legendLayout.addWidget(legend) if self.__lib == 'Qwt5': # Split xx and yy into single lines at None values xx = [list(g) for k, g in itertools.groupby(xx, lambda x: x is None) if not k] yy = [list(g) for k, g in itertools.groupby(yy, lambda x: x is None) if not k] # Create & attach one QwtPlotCurve per one single line for j in range(len(xx)): curve = QwtPlotCurve(name) curve.setData(xx[j], yy[j]) curve.setPen(QPen(QColor(self.__colors[c]), 3)) if i > (self.__numLines-1): curve.setStyle(QwtPlotCurve.Dots) pen = QPen(QColor(self.__colors[c]), 8) pen.setCapStyle(Qt.RoundCap) curve.setPen(pen) curve.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': qcol = QColor(self.__colors[c]) if i < self.__numLines: self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=3) else: self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=5, marker='o', linestyle='None') tmp = self.__plotWdg.figure.get_axes()[0].get_lines() for t in range(len(tmp)): if name == tmp[t].get_gid(): tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0), old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0))) self.__plotWdg.draw() break c += 1 # scaling this try: self.__reScalePlot(None, True) except: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Rescale problem... (trace printed)"), level=QgsMessageBar.CRITICAL, duration=0) print(sys.exc_info()[0], traceback.format_exc()) if self.__lib == 'Qwt5': self.__plotWdg.replot() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].redraw_in_frame() self.__plotWdg.draw() self.__activateMouseTracking(True) self.__marker.show() def __reScalePlot(self, value=None, auto=False): """ To rescale the profile plot depending to the bounds :param value: juste because connections give value :param auto: if automatic ranges calcul is wanted """ if (self.__profiles is None) or (self.__profiles == 0): self.__plotWdg.replot() return maxi = 0 for i in range(len(self.__profiles)): if (ceil(self.__profiles[i]['l'])) > maxi: maxi = ceil(self.__profiles[i]['l']) if self.__lib == 'Qwt5': self.__plotWdg.setAxisScale(2, 0, maxi, 0) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].set_xbound(0, maxi) minimumValue = self.__minSpin.value() maximumValue = self.__maxSpin.value() # to set max y and min y displayed if auto: minimumValue = 1000000000 maximumValue = -1000000000 for i in range(len(self.__profiles)): if 'z' in self.__profiles[i]: mini = self.__minTab(self.__profiles[i]['z']) if (mini > 0 or self.__displayZeros) and mini < minimumValue: minimumValue = ceil(mini) - 1 maxi = self.__maxTab(self.__profiles[i]['z']) if maxi > maximumValue: maximumValue = floor(maxi) + 1 if self.__mntPoints is not None: for pts in self.__mntPoints[2]: miniMnt = self.__minTab(pts) if (miniMnt > 0 or self.__displayZeros) and miniMnt < minimumValue: minimumValue = ceil(miniMnt) - 1 maxiMnt = self.__maxTab(pts) if maxiMnt > maximumValue: maximumValue = floor(maxiMnt) + 1 self.__maxSpin.setValue(maximumValue) self.__minSpin.setValue(minimumValue) self.__maxSpin.setEnabled(True) self.__minSpin.setEnabled(True) if self.__lib == 'Qwt5': rect = QRectF(0, minimumValue, maxi, maximumValue-minimumValue) self.__zoomer.setZoomBase(rect) # to draw vertical lines for i in range(len(self.__profiles)): zz = [] for j in range(self.__numLines): if self.__profiles[i]['z'][j] is not None: zz.append(j) color = None if len(zz) == 2: width = 3 color = QColor('red') else: width = 1 if self.__lib == 'Qwt5': vertLine = QwtPlotMarker() vertLine.setLineStyle(QwtPlotMarker.VLine) pen = vertLine.linePen() pen.setWidth(width) if color is not None: pen.setColor(color) vertLine.setLinePen(pen) vertLine.setXValue(self.__profiles[i]['l']) label = vertLine.label() label.setText(str(i)) vertLine.setLabel(label) vertLine.setLabelAlignment(Qt.AlignLeft) vertLine.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].vlines(self.__profiles[i]['l'], minimumValue, maximumValue, linewidth=width) if minimumValue < maximumValue: if self.__lib == 'Qwt5': self.__plotWdg.setAxisScale(0, minimumValue, maximumValue, 0) self.__plotWdg.replot() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].set_ybound(minimumValue, maximumValue) self.__plotWdg.figure.get_axes()[0].redraw_in_frame() self.__plotWdg.draw() @staticmethod def __minTab(tab): """ To get the minimum value in a table :param tab: table to scan :return: minimum value """ mini = 1000000000 for t in tab: if t is None: continue if t < mini: mini = t return mini @staticmethod def __maxTab(tab): """ To get the maximum value in a table :param tab: table to scan :return: maximum value """ maxi = -1000000000 for t in tab: if t is None: continue if t > maxi: maxi = t return maxi def __setLib(self): """ To set the new widget library (qwt <-> matplotlib) """ self.__lib = self.__libs[self.__libCombo.currentIndex()] self.__changePlotWidget() def __save(self): """ To save the profile in a file, on selected format """ idx = self.__typeCombo.currentIndex() if idx == 0: self.__outPDF() elif idx == 1: self.__outPNG() else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Invalid index ") + str(idx), level=QgsMessageBar.CRITICAL, duration=0) def __outPDF(self): """ To save the profile as pdf file """ fileName = QFileDialog.getSaveFileName( self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"), QCoreApplication.translate("VDLTools", "Profile.pdf"),"Portable Document Format (*.pdf)") if fileName is not None: if self.__lib == 'Qwt5': printer = QPrinter() printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin")) printer.setOutputFileName(fileName) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOrientation(QPrinter.Landscape) self.__plotWdg.print_(printer) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.savefig(str(fileName)) def __outPNG(self): """ To save the profile as png file """ fileName = QFileDialog.getSaveFileName( self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"), QCoreApplication.translate("VDLTools", "Profile.png"),"Portable Network Graphics (*.png)") if fileName is not None: QPixmap.grabWidget(self.__printWdg).save(fileName, "PNG") def clearData(self): """ To clear the displayed data """ if self.__profiles is None: return if self.__lib == 'Qwt5': self.__plotWdg.clear() self.__profiles = None temp1 = self.__plotWdg.itemList() for j in range(len(temp1)): if temp1[j].rtti() == QwtPlotItem.Rtti_PlotCurve: temp1[j].detach() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].cla() self.__manageMatplotlibAxe(self.__plotWdg.figure.get_axes()[0]) self.__maxSpin.setEnabled(False) self.__minSpin.setEnabled(False) self.__maxSpin.setValue(0) self.__minSpin.setValue(0) # clear legend while self.__legendLayout.count(): child = self.__legendLayout.takeAt(0) child.widget().deleteLater() def __manageMatplotlibAxe(self, axe): """ To manage the axes for matplotlib library :param axe: the axes element """ axe.grid() axe.tick_params(axis="both", which="major", direction="out", length=10, width=1, bottom=True, top=False, left=True, right=False) axe.minorticks_on() axe.tick_params(axis="both", which="minor", direction="out", length=5, width=1, bottom=True, top=False, left=True, right=False) axe.set_xlabel(QCoreApplication.translate("VDLTools", "Distance [m]")) axe.set_ylabel(QCoreApplication.translate("VDLTools", "Elevation [m]")) def __activateMouseTracking(self, activate): """ To (de)activate the mouse tracking on the profile for matplotlib library :param activate: true to activate, false to deactivate """ if activate: self.__doTracking = True self.__loadRubber() self.cid = self.__plotWdg.mpl_connect('motion_notify_event', self.__mouseevent_mpl) elif self.__doTracking: self.__doTracking = False self.__plotWdg.mpl_disconnect(self.cid) if self.__marker is not None: self.__canvas.scene().removeItem(self.__marker) try: if self.__vline is not None: self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline) self.__plotWdg.draw() except Exception as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Tracking exception : ") + str(e), level=QgsMessageBar.CRITICAL, duration=0) def __mouseevent_mpl(self, event): """ To manage matplotlib mouse tracking event :param event: mouse tracking event """ if event.xdata is not None: try: if self.__vline is not None: self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline) except Exception as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Mouse event exception : ") + str(e), level=QgsMessageBar.CRITICAL, duration=0) xdata = float(event.xdata) self.__vline = self.__plotWdg.figure.get_axes()[0].axvline(xdata, linewidth=2, color='k') self.__plotWdg.draw() i = 1 while i < len(self.__tabmouseevent)-1 and xdata > self.__tabmouseevent[i][0]: i += 1 i -= 1 x = self.__tabmouseevent[i][1] + (self.__tabmouseevent[i + 1][1] - self.__tabmouseevent[i][1]) / ( self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0]) y = self.__tabmouseevent[i][2] + (self.__tabmouseevent[i + 1][2] - self.__tabmouseevent[i][2]) / ( self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0]) self.__marker.show() self.__marker.setCenter(QgsPoint(x, y)) def __loadRubber(self): """ To load te rubber band for mouse tracking on map """ self.__marker = QgsVertexMarker(self.__canvas) self.__marker.setIconSize(5) self.__marker.setIconType(QgsVertexMarker.ICON_BOX) self.__marker.setPenWidth(3) def __prepare_points(self): """ To prepare the points on map for mouse tracking on profile """ self.__tabmouseevent = [] length = 0 for i, point in enumerate(self.__profiles): if i == 0: self.__tabmouseevent.append([0, point['x'], point['y']]) else: length += ((self.__profiles[i]['x'] - self.__profiles[i-1]['x']) ** 2 + (self.__profiles[i]['y'] - self.__profiles[i-1]['y']) ** 2) ** 0.5 self.__tabmouseevent.append([float(length), float(point['x']), float(point['y'])]) def closeEvent(self, event): """ When the dock widget is closed :param event: close event """ if self.__maxSpin is not None: Signal.safelyDisconnect(self.__maxSpin.valueChanged, self.__reScalePlot) self.__maxSpin = None if self.__minSpin is not None: Signal.safelyDisconnect(self.__minSpin.valueChanged, self.__reScalePlot) self.__minSpin = None if self.__saveButton is not None: Signal.safelyDisconnect(self.__saveButton.clicked, self.__save) self.__saveButton = None if self.__libCombo is not None: Signal.safelyDisconnect(self.__libCombo.currentIndexChanged, self.__setLib) self.__libCombo = None self.closeSignal.emit() if self.__marker is not None: self.__marker.hide() QDockWidget.closeEvent(self, event)
class StartDrawing(QgsMapToolEmitPoint): def __init__(self, canvas, iface): QgsMapToolEmitPoint.__init__(self, canvas) # qgis interface self.canvas = canvas self.iface = iface # snap marker self.snap_mark = QgsVertexMarker(self.canvas) self.snap_mark.setColor(QColor(0, 0, 255)) self.snap_mark.setPenWidth(2) self.snap_mark.setIconType(QgsVertexMarker.ICON_BOX) self.snap_mark.setIconSize(10) # rectangle self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.GeometryType(3)) self.rubberBand.setWidth(3) self.rubberBand.setStrokeColor(QColor(254, 0, 0)) # initialize variable self.rubberBand_width = 0 self.rubberBand_height = 0 self.rubberBand_angle = 0 self.reset() def setConfiguration(self, width, height, angle): self.rubberBand_width = width self.rubberBand_height = height self.rubberBand_angle = angle def reset(self): self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) self.isEmittingPoint = False def canvasMoveEvent(self, e): self.snap_mark.hide() self.snapPoint = False self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) self.snapPoint = self.checkSnapToPoint(e.pos()) if self.snapPoint[0]: self.snap_mark.setCenter(self.snapPoint[1]) self.snap_mark.show() self.rectangle = self.getRectangle(self.snapPoint[1]) self.rubberBand.setToGeometry(self.rectangle, None) self.rubberBand.show() else: self.rectangle = self.getRectangle(self.toMapCoordinates(e.pos())) self.rubberBand.setToGeometry(self.rectangle, None) self.rubberBand.show() def checkSnapToPoint(self, point): snapped = False snap_point = self.toMapCoordinates(point) snapper = self.canvas.snappingUtils() snapMatch = snapper.snapToMap(point) if snapMatch.hasVertex(): snap_point = snapMatch.point() snapped = True return snapped, snap_point def canvasPressEvent(self, e): if self.snapPoint == False: point = self.toMapCoordinates(self.canvas.mouseLastXY()) else: point = self.snapPoint[1] layer = self.iface.activeLayer() layer_crs = layer.crs().authid() layer_crs = QgsCoordinateReferenceSystem(layer_crs) canvas_crs = self.canvas.mapSettings().destinationCrs().authid() canvas_crs = QgsCoordinateReferenceSystem(canvas_crs) crs2crs = QgsCoordinateTransform(canvas_crs, layer_crs, QgsProject.instance()) feature = QgsFeature() fields = layer.fields() feature.setFields(fields) if layer.wkbType() == QgsWkbTypes.Polygon or layer.wkbType( ) == QgsWkbTypes.MultiPolygon: if layer_crs == canvas_crs: feature.setGeometry(self.rectangle) else: geom = self.rectangle geom.transform(crs2crs) feature.setGeometry(geom) layer.startEditing() layer.addFeature(feature) layer.commitChanges() layer.reload() else: self.iface.messageBar().pushCritical( 'QRectangle Creator: ', 'The current layer is not of Polygon or MultiPolygon type. The object has not been added' ) def getRectangle(self, point): polygon = QgsWkbTypes.GeometryType(3) x = point.x() y = point.y() points = [[ QgsPointXY( # Left Top corner x - (self.rubberBand_width / 2), y + (self.rubberBand_height / 2)), QgsPointXY( # Right Top corner x + (self.rubberBand_width / 2), y + (self.rubberBand_height / 2)), QgsPointXY( # Right Down corner x + (self.rubberBand_width / 2), y - (self.rubberBand_height / 2)), QgsPointXY( # Left Down corner x - (self.rubberBand_width / 2), y - (self.rubberBand_height / 2)) ]] polygon = QgsGeometry.fromPolygonXY(points) polygon.rotate(self.rubberBand_angle, point) return polygon
class ParentMapTool(QgsMapTool): def __init__(self, iface, settings, action, index_action): """ Class constructor """ self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.show_help = bool(int(self.settings.value('status/show_help', 1))) self.index_action = index_action self.layer_arc = None self.layer_connec = None self.layer_node = None self.layer_arc_man = None self.layer_connec_man = None self.layer_node_man = None self.layer_gully_man = None self.schema_name = None self.controller = None self.dao = None # Call superclass constructor and set current action QgsMapTool.__init__(self, self.canvas) self.setAction(action) # Snapper self.snapper_manager = SnappingConfigManager(self.iface) self.snapper = QgsMapCanvasSnapper(self.canvas) # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) # Get default cursor self.std_cursor = self.parent().cursor() # Set default vertex marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) # Set default rubber band color_selection = QColor(254, 178, 76, 63) self.rubber_band = QgsRubberBand(self.canvas, QGis.Polygon) self.rubber_band.setColor(color) self.rubber_band.setFillColor(color_selection) self.rubber_band.setWidth(1) self.reset() self.force_active_layer = True # Set default encoding reload(sys) sys.setdefaultencoding('utf-8') #@UndefinedVariable def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def set_layers(self, layer_arc_man, layer_connec_man, layer_node_man, layer_gully_man=None): """ Sets layers involved in Map Tools functions Sets Snapper Manager """ self.layer_arc_man = layer_arc_man self.layer_connec_man = layer_connec_man self.layer_node_man = layer_node_man self.layer_gully_man = layer_gully_man self.snapper_manager.set_layers(layer_arc_man, layer_connec_man, layer_node_man, layer_gully_man) def set_controller(self, controller): self.controller = controller self.schema_name = controller.schema_name self.plugin_dir = self.controller.plugin_dir self.snapper_manager.controller = controller def deactivate(self): # Uncheck button self.action().setChecked(False) # Restore previous snapping self.snapper_manager.recover_snapping_options() # Recover cursor self.canvas.setCursor(self.std_cursor) # Remove highlight self.vertex_marker.hide() def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def set_action_pan(self): """ Set action 'Pan' """ try: self.iface.actionPan().trigger() except Exception: pass def reset(self): # Graphic elements self.rubber_band.reset(QGis.Polygon) # Selection self.snapped_feat = None def cancel_map_tool(self): """ Executed if user press right button or escape key """ # Reset rubber band self.reset() # Deactivate map tool self.deactivate() self.set_action_pan() def remove_markers(self): """ Remove previous markers """ vertex_items = [ i for i in self.canvas.scene().items() if issubclass(type(i), QgsVertexMarker) ] for ver in vertex_items: if ver in self.canvas.scene().items(): self.canvas.scene().removeItem(ver) def refresh_map_canvas(self): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() def open_dialog(self, dlg=None, dlg_name=None, maximize_button=True, stay_on_top=True): """ Open dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top and maximize button if maximize_button and stay_on_top: dlg.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowMaximizeButtonHint | Qt.WindowStaysOnTopHint) elif not maximize_button and stay_on_top: dlg.setWindowFlags(Qt.WindowMinimizeButtonHint | Qt.WindowStaysOnTopHint) elif maximize_button and not stay_on_top: dlg.setWindowFlags(Qt.WindowMaximizeButtonHint) # Open dialog dlg.open() def close_dialog(self, dlg=None, set_action_pan=True): """ Close dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg try: self.save_settings(dlg) dlg.close() if set_action_pan: map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.set_action_pan() except AttributeError: pass def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: width = self.controller.plugin_settings_value( dialog.objectName() + "_width", dialog.width()) height = self.controller.plugin_settings_value( dialog.objectName() + "_height", dialog.height()) x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: self.controller.plugin_settings_set_value( dialog.objectName() + "_width", dialog.width()) self.controller.plugin_settings_set_value( dialog.objectName() + "_height", dialog.height()) self.controller.plugin_settings_set_value( dialog.objectName() + "_x", dialog.pos().x()) self.controller.plugin_settings_set_value( dialog.objectName() + "_y", dialog.pos().y()) except: pass def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return (False, expr) return (True, expr) def canvasMoveEvent(self, event): # Make sure active layer is always 'v_edit_node' cur_layer = self.iface.activeLayer() if cur_layer != self.layer_node and self.force_active_layer: self.iface.setActiveLayer(self.layer_node) # Hide highlight self.vertex_marker.hide() try: # Get current mouse coordinates x = event.pos().x() y = event.pos().y() event_point = QPoint(x, y) except (TypeError, KeyError): self.iface.actionPan().trigger() return # Snapping (retval, result) = self.snapper.snapToCurrentLayer(event_point, 2) # @UnusedVariable # That's the snapped features if result: # Get the point and add marker on it point = QgsPoint(result[0].snappedVertex) self.vertex_marker.setCenter(point) self.vertex_marker.show()
class DeleteTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.elev = -1 self.vertex_marker = QgsVertexMarker(self.canvas()) # Stroke self.rubber_band = QgsRubberBand(self.data_dock.iface.mapCanvas()) self.rubber_band.setColor(QColor(255, 0, 0, 255)) self.rubber_band.setWidth(2) self.mouse_clicked = False self.snapper = None self.clicked_pt = None self.snap_results = None self.adj_links_fts = None self.selected_node_ft = None self.selected_node_ft_lay = None self.mouse_pt = None self.pump_valve_selected = False self.pump_or_valve = None self.pump_valve_ft = None self.adj_junctions = None self.delta_vec = None self.adj_pipes_fts_d = {} def canvasPressEvent(self, event): # if self.snap_results is None: # return if event.button() == Qt.RightButton: self.mouse_clicked = False self.clicked_pt = None if event.button() == Qt.LeftButton: self.mouse_clicked = True self.clicked_pt = self.toMapCoordinates(event.pos()) def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) if elev is not None: self.elev = elev self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) # Mouse not clicked if not self.mouse_clicked: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): self.snap_results = match # snapped_pt = self.snap_results[0].snappedVertex self.snapped_pipe_id = match.featureId() snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter( QgsPointXY(snapped_vertex.x(), snapped_vertex.y())) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType( QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: self.snap_results = None self.selected_node_ft = None self.vertex_marker.hide() # Mouse clicked: draw rectangle else: if self.snap_results is None: end_point = self.toMapCoordinates(event.pos()) self.show_rect(self.clicked_pt, end_point) def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # Snapped: one element selected if self.snap_results is not None: selected_node_ft_lay, selected_node_ft = vector_utils.findSnappedNode( self.snapper, self.snap_results, self.params) # A node ha been snapped if selected_node_ft_lay is not None: self.delete_element(selected_node_ft_lay, selected_node_ft) # A link has been snapped else: snapped_ft = vector_utils.get_feats_by_id( self.snap_results.layer(), self.snap_results.featureId()) snapped_layer = self.snap_results.layer() self.delete_element(snapped_layer, snapped_ft[0]) # Not snapped: rectangle else: rubber_band_rect = self.rubber_band.asGeometry().boundingBox() self.rubber_band.reset() self.delete_elements(self.params.valves_vlay, rubber_band_rect) self.delete_elements(self.params.pumps_vlay, rubber_band_rect) self.delete_elements(self.params.pipes_vlay, rubber_band_rect) self.delete_elements(self.params.tanks_vlay, rubber_band_rect) self.delete_elements(self.params.reservoirs_vlay, rubber_band_rect) self.delete_elements(self.params.junctions_vlay, rubber_band_rect) # Refresh symbology.refresh_layer(self.iface.mapCanvas(), self.params.junctions_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.reservoirs_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.tanks_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pipes_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.pumps_vlay) symbology.refresh_layer(self.iface.mapCanvas(), self.params.valves_vlay) def activate(self): cursor = QCursor() cursor.setShape(Qt.ArrowCursor) self.iface.mapCanvas().setCursor(cursor) layers = { self.params.junctions_vlay: QgsSnappingConfig.Vertex, self.params.reservoirs_vlay: QgsSnappingConfig.Vertex, self.params.tanks_vlay: QgsSnappingConfig.Vertex, self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment } self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.reservoirs_vlay.isEditable(): self.params.reservoirs_vlay.startEditing() if not self.params.tanks_vlay.isEditable(): self.params.tanks_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.pumps_vlay.isEditable(): self.params.pumps_vlay.startEditing() if not self.params.valves_vlay.isEditable(): self.params.valves_vlay.startEditing() def deactivate(self): self.data_dock.btn_delete_element.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def show_rect(self, start_point, end_point): self.rubber_band.reset() if start_point.x() == end_point.x() or start_point.y() == end_point.y( ): return point1 = QgsPointXY(start_point.x(), start_point.y()) point2 = QgsPointXY(start_point.x(), end_point.y()) point3 = QgsPointXY(end_point.x(), end_point.y()) point4 = QgsPointXY(end_point.x(), start_point.y()) point5 = QgsPointXY(start_point.x(), start_point.y()) self.rubber_band.addPoint(point1, False) self.rubber_band.addPoint(point2, False) self.rubber_band.addPoint(point3, False) self.rubber_band.addPoint(point4, False) self.rubber_band.closePoints(True) # true to update canvas self.rubber_band.show() def delete_elements(self, layer, rectangle): try: QApplication.setOverrideCursor(Qt.WaitCursor) feats = layer.getFeatures() for feat in feats: if rectangle.contains(feat.geometry().boundingBox()): self.delete_element(layer, feat) finally: QApplication.restoreOverrideCursor() def delete_element(self, layer, feature): # If node if layer == self.params.junctions_vlay or \ layer == self.params.reservoirs_vlay or \ layer == self.params.tanks_vlay: # The node is a junction if layer == self.params.junctions_vlay: adj_links_fts = NetworkUtils.find_adjacent_links( self.params, feature.geometry()) # Only pipes adjacent to node: it's a simple junction if not adj_links_fts['pumps'] and not adj_links_fts['valves']: # Delete node NodeHandler.delete_node(self.params, layer, feature) # Delete adjacent pipes adj_pipes = NetworkUtils.find_adjacent_links( self.params, feature.geometry()) for adj_pipe in adj_pipes['pipes']: LinkHandler.delete_link(self.params.pipes_vlay, adj_pipe) # The node is part of a pump or valve else: if adj_links_fts['pumps']: LinkHandler.delete_link(self.params, self.params.pumps_vlay, feature) elif adj_links_fts['valves']: LinkHandler.delete_link(self.params, self.params.valves_vlay, feature) # The node is a reservoir or a tank elif layer == self.params.reservoirs_vlay or \ layer == self.params.tanks_vlay: adj_pipes = NetworkUtils.find_adjacent_links( self.params, feature.geometry())['pipes'] NodeHandler._delete_feature(self.params, layer, feature) for adj_pipe in adj_pipes: LinkHandler.delete_link(self.params, self.params.pipes_vlay, adj_pipe) # If pipe elif layer == self.params.pipes_vlay: if self.snap_results is not None: vertex = feature.geometry().closestVertexWithContext( self.snap_results.point()) vertex_dist = vertex[0] if vertex_dist < self.params.min_dist: # Delete vertex LinkHandler.delete_vertex(self.params, self.params.pipes_vlay, feature, vertex[1]) else: # Delete whole feature LinkHandler.delete_link(self.params, layer, feature) else: LinkHandler.delete_link(self.params, layer, feature)
class ParentMapTool(QgsMapTool): def __init__(self, iface, settings, action, index_action): """ Class constructor """ self.iface = iface self.canvas = self.iface.mapCanvas() self.settings = settings self.show_help = bool(int(self.settings.value('status/show_help', 1))) self.index_action = index_action self.layer_arc = None self.layer_connec = None self.layer_gully = None self.layer_node = None self.schema_name = None self.controller = None self.dao = None self.snapper_manager = None # Call superclass constructor and set current action QgsMapTool.__init__(self, self.canvas) self.setAction(action) # Change map tool cursor self.cursor = QCursor() self.cursor.setShape(Qt.CrossCursor) # Get default cursor # noinspection PyCallingNonCallable self.std_cursor = self.parent().cursor() # Set default vertex marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) # Set default rubber band color_selection = QColor(254, 178, 76, 63) self.rubber_band = QgsRubberBand(self.canvas, 2) self.rubber_band.setColor(color) self.rubber_band.setFillColor(color_selection) self.rubber_band.setWidth(1) self.reset() self.force_active_layer = True def get_cursor_multiple_selection(self): """ Set cursor for multiple selection """ path_folder = os.path.join(os.path.dirname(__file__), os.pardir) path_cursor = os.path.join(path_folder, 'icons', '201.png') if os.path.exists(path_cursor): cursor = QCursor(QPixmap(path_cursor)) else: cursor = QCursor(Qt.ArrowCursor) return cursor def set_controller(self, controller): self.controller = controller self.schema_name = controller.schema_name self.plugin_dir = self.controller.plugin_dir if self.snapper_manager is None: self.snapper_manager = SnappingConfigManager(self.iface) self.snapper_manager.controller = controller def deactivate(self): # Uncheck button self.action().setChecked(False) # Restore previous snapping self.snapper_manager.recover_snapping_options() # Enable snapping self.snapper_manager.enable_snapping(True) # Recover cursor self.canvas.setCursor(self.std_cursor) # Remove highlight self.vertex_marker.hide() def recover_previus_maptool(self): if self.controller.prev_maptool: self.iface.mapCanvas().setMapTool(self.controller.prev_maptool) self.controller.prev_maptool = None def remove_vertex(self): """ Remove vertex_marker from canvas""" vertex_items = [ i for i in self.iface.mapCanvas().scene().items() if issubclass(type(i), QgsVertexMarker) ] for ver in vertex_items: if ver in self.iface.mapCanvas().scene().items(): if self.vertex_marker == ver: self.iface.mapCanvas().scene().removeItem(ver) def set_icon(self, widget, icon): """ Set @icon to selected @widget """ # Get icons folder icons_folder = os.path.join(self.plugin_dir, 'icons') icon_path = os.path.join(icons_folder, str(icon) + ".png") if os.path.exists(icon_path): widget.setIcon(QIcon(icon_path)) else: self.controller.log_info("File not found", parameter=icon_path) def set_action_pan(self): """ Set action 'Pan' """ try: self.iface.actionPan().trigger() except Exception: pass def reset_rubber_band(self, geom_type="polygon"): try: if geom_type == "polygon": geom_type = QgsWkbTypes.PolygonGeometry elif geom_type == "line": geom_type = QgsWkbTypes.LineString self.rubber_band.reset(geom_type) except: pass def reset(self): self.reset_rubber_band() self.snapped_feat = None def cancel_map_tool(self): """ Executed if user press right button or escape key """ # Reset rubber band self.reset() # Deactivate map tool self.deactivate() self.set_action_pan() def remove_markers(self): """ Remove previous markers """ vertex_items = [ i for i in list(self.canvas.scene().items()) if issubclass(type(i), QgsVertexMarker) ] for ver in vertex_items: if ver in list(self.canvas.scene().items()): self.canvas.scene().removeItem(ver) def refresh_map_canvas(self): """ Refresh all layers present in map canvas """ self.canvas.refreshAllLayers() for layer_refresh in self.canvas.layers(): layer_refresh.triggerRepaint() def open_dialog(self, dlg=None, dlg_name=None, info=True, maximize_button=True, stay_on_top=True): """ Open dialog """ # Check database connection before opening dialog if not self.controller.check_db_connection(): return if dlg is None or type(dlg) is bool: dlg = self.dlg # Manage i18n of the dialog if dlg_name: self.controller.manage_translation(dlg_name, dlg) # Manage stay on top, maximize/minimize button and information button # if info is True maximize flag will be ignored # To enable maximize button you must set info to False flags = Qt.WindowCloseButtonHint if info: flags |= Qt.WindowSystemMenuHint | Qt.WindowContextHelpButtonHint else: if maximize_button: flags |= Qt.WindowMinMaxButtonsHint if stay_on_top: flags |= Qt.WindowStaysOnTopHint dlg.setWindowFlags(flags) # Open dialog if issubclass(type(dlg), GwDialog): dlg.open() elif issubclass(type(dlg), GwMainWindow): dlg.show() else: dlg.show() def close_dialog(self, dlg=None, set_action_pan=True): """ Close dialog """ if dlg is None or type(dlg) is bool: dlg = self.dlg try: self.save_settings(dlg) dlg.close() if set_action_pan: map_tool = self.canvas.mapTool() # If selected map tool is from the plugin, set 'Pan' as current one if map_tool.toolName() == '': self.set_action_pan() except AttributeError: pass def load_settings(self, dialog=None): """ Load QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: x = self.controller.plugin_settings_value(dialog.objectName() + "_x") y = self.controller.plugin_settings_value(dialog.objectName() + "_y") width = self.controller.plugin_settings_value( dialog.objectName() + "_width", dialog.property('width')) height = self.controller.plugin_settings_value( dialog.objectName() + "_height", dialog.property('height')) if int(x) < 0 or int(y) < 0: dialog.resize(int(width), int(height)) else: screens = ctypes.windll.user32 screen_x = screens.GetSystemMetrics(78) screen_y = screens.GetSystemMetrics(79) if int(x) > screen_x: x = int(screen_x) - int(width) if int(y) > screen_y: y = int(screen_y) dialog.setGeometry(int(x), int(y), int(width), int(height)) except: pass def save_settings(self, dialog=None): """ Save QGIS settings related with dialog position and size """ if dialog is None: dialog = self.dlg try: self.controller.plugin_settings_set_value( dialog.objectName() + "_width", dialog.property('width')) self.controller.plugin_settings_set_value( dialog.objectName() + "_height", dialog.property('height')) self.controller.plugin_settings_set_value( dialog.objectName() + "_x", dialog.pos().x()) self.controller.plugin_settings_set_value( dialog.objectName() + "_y", dialog.pos().y()) except: pass def check_expression(self, expr_filter, log_info=False): """ Check if expression filter @expr is valid """ if log_info: self.controller.log_info(expr_filter) expr = QgsExpression(expr_filter) if expr.hasParserError(): message = "Expression Error" self.controller.log_warning(message, parameter=expr_filter) return False, expr return True, expr def get_composers_list(self): layour_manager = QgsProject.instance().layoutManager().layouts() active_composers = [layout for layout in layour_manager] return active_composers def get_composer_index(self, name): index = 0 composers = self.get_composers_list() for comp_view in composers: composer_name = comp_view.name() if composer_name == name: break index += 1 return index def canvasMoveEvent(self, event): # Make sure active layer is always 'v_edit_node' cur_layer = self.iface.activeLayer() if cur_layer != self.layer_node and self.force_active_layer: self.iface.setActiveLayer(self.layer_node) # Hide highlight and get coordinates self.vertex_marker.hide() event_point = self.snapper_manager.get_event_point(event) # Snapping result = self.snapper_manager.snap_to_current_layer(event_point) if self.snapper_manager.result_is_valid(): self.snapper_manager.add_marker(result, self.vertex_marker) def create_body(self, form='', feature='', filter_fields='', extras=None): """ Create and return parameters as body to functions""" client = f'$${{"client":{{"device":4, "infoType":1, "lang":"ES"}}, ' form = f'"form":{{{form}}}, ' feature = '"feature":{' + feature + '}, ' filter_fields = '"filterFields":{' + filter_fields + '}' page_info = '"pageInfo":{}' data = '"data":{' + filter_fields + ', ' + page_info if extras: data += ', ' + extras data += f'}}}}$$' body = "" + client + form + feature + data return body def refresh_legend(self): """ This function solves the bug generated by changing the type of feature. Mysteriously this bug is solved by checking and unchecking the categorization of the tables. # TODO solve this bug """ layers = [ self.controller.get_layer_by_tablename('v_edit_node'), self.controller.get_layer_by_tablename('v_edit_connec'), self.controller.get_layer_by_tablename('v_edit_gully') ] for layer in layers: if layer: ltl = QgsProject.instance().layerTreeRoot().findLayer( layer.id()) ltm = self.iface.layerTreeView().model() legendNodes = ltm.layerLegendNodes(ltl) for ln in legendNodes: current_state = ln.data(Qt.CheckStateRole) ln.setData(Qt.Unchecked, Qt.CheckStateRole) ln.setData(Qt.Checked, Qt.CheckStateRole) ln.setData(current_state, Qt.CheckStateRole) def put_layer_into_toc(self, tablename=None, the_geom="the_geom", field_id="id", group='GW Layers'): """ Put layer from postgres DB into TOC""" schema_name = self.controller.credentials['schema'].replace('"', '') uri = QgsDataSourceUri() uri.setConnection(self.controller.credentials['host'], self.controller.credentials['port'], self.controller.credentials['db'], self.controller.credentials['user'], self.controller.credentials['password']) if not field_id: field_id = self.controller.get_pk(tablename) if not field_id: field_id = "id" uri.setDataSource(schema_name, f'{tablename}', the_geom, None, field_id) layer = QgsVectorLayer(uri.uri(), f'{tablename}', "postgres") root = QgsProject.instance().layerTreeRoot() my_group = root.findGroup(group) if my_group is None: my_group = root.insertGroup(0, group) my_group.insertLayer(0, layer) self.iface.mapCanvas().refresh() return layer def populate_info_text(self, dialog, data, force_tab=True, reset_text=True, tab_idx=1): change_tab = False text = qt_tools.getWidgetText(dialog, 'txt_infolog', return_string_null=False) if reset_text: text = "" for item in data['info']['values']: if 'message' in item: if item['message'] is not None: text += str(item['message']) + "\n" if force_tab: change_tab = True else: text += "\n" qt_tools.setWidgetText(dialog, 'txt_infolog', text + "\n") qtabwidget = dialog.findChild(QTabWidget, 'mainTab') if change_tab and qtabwidget is not None: qtabwidget.setCurrentIndex(tab_idx) return change_tab
class MincutMapTool(ParentMapTool): ''' Button 26. User select one node or arc. Execute SQL function: 'gw_fct_mincut' This function fills 3 temporary tables with id's: node_id, arc_id and valve_id Returns and integer: error code Get these id's and select them in its corresponding layers ''' def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' # Call ParentMapTool constructor super(MincutMapTool, self).__init__(iface, settings, action, index_action) # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(11) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) ''' QgsMapTools inherited event functions ''' def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Snapping (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable self.current_layer = None # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name() or snapPoint.layer.name() == self.layer_arc.name(): # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() # Data for function self.current_layer = result[0].layer self.snappFeat = next(result[0].layer.getFeatures(QgsFeatureRequest().setFilterFid(result[0].snappedAtGeometry))) # Change symbol if snapPoint.layer.name() == self.layer_node.name(): self.vertexMarker.setIconType(QgsVertexMarker.ICON_CIRCLE) else: self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) break def canvasReleaseEvent(self, event): ''' With left click the digitizing is finished ''' if event.button() == Qt.LeftButton and self.current_layer is not None: # Get selected layer type: 'arc' or 'node' if self.current_layer.name() == self.layer_arc.name(): elem_type = 'arc' elif self.current_layer.name() == self.layer_node.name(): elem_type = 'node' else: message = "Current layer not valid" self.controller.show_warning(message, context_name='ui_message') return feature = self.snappFeat elem_id = feature.attribute(elem_type+'_id') # Execute SQL function function_name = "gw_fct_mincut" sql = "SELECT "+self.schema_name+"."+function_name+"('"+str(elem_id)+"', '"+elem_type+"');" result = self.controller.execute_sql(sql) print sql if result: # Get 'arc' and 'node' list and select them self.mg_flow_trace_select_features(self.layer_arc, 'arc') self.mg_flow_trace_select_features(self.layer_node, 'node') # Refresh map canvas self.iface.mapCanvas().refresh() def mg_flow_trace_select_features(self, layer, elem_type): sql = "SELECT * FROM "+self.schema_name+".anl_mincut_"+elem_type+" ORDER BY "+elem_type+"_id" rows = self.controller.get_rows(sql) if rows: # Build an expression to select them aux = "\""+elem_type+"_id\" IN (" for elem in rows: aux += elem[0] + ", " aux = aux[:-2] + ")" # Get a featureIterator from this expression: expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error: " + str(expr.parserErrorString()) self.controller.show_warning(message, context_name='ui_message') return it = layer.getFeatures(QgsFeatureRequest(expr)) # Build a list of feature id's from the previous result id_list = [i.id() for i in it] # Select features with these id's layer.setSelectedFeatures(id_list) def activate(self): # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to arc and node self.snapperManager.snapToArc() self.snapperManager.snapToNode() # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = "Select a node or pipe and click on it, the valves minimum cut polygon is computed" self.controller.show_info(message, context_name='ui_message' ) # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_arc) except: self.canvas.setCurrentLayer(self.layer_arc) def deactivate(self): # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) # Remove highlight self.h = None
class MyBoton2(dialog.GwAction): def __init__(self, icon_path, action_name, text, toolbar, action_group): super().__init__(icon_path, action_name, text, toolbar, action_group) def clicked_event(self): self.selection_type = SelectionType.ACTIVE self.dlg_btn2 = DlgBoton2() tools_gw.load_settings(self.dlg_btn2) # Secció: selecció de capes self.dlg_btn2.rdb_layers_active.clicked.connect( partial(self.selection_type_changed, SelectionType.ACTIVE)) self.dlg_btn2.rdb_layers_all.clicked.connect( partial(self.selection_type_changed, SelectionType.ALL)) # Secció: activar l'estat de "seleccionant al mapa" self.dlg_btn2.btn_select.clicked.connect(self.selection_start) self.dlg_btn2.btn_cancel.clicked.connect(self.deactivate_signals) self.dlg_btn2.btn_cancel.clicked.connect( lambda: self.dlg_btn2.rdb_layers_active.setEnabled(True)) self.dlg_btn2.btn_cancel.clicked.connect( lambda: self.dlg_btn2.rdb_layers_all.setEnabled(True)) # Secció: sortida self.dlg_btn2.btn_close.clicked.connect(self.dlg_btn2.close) self.dlg_btn2.rejected.connect( partial(tools_gw.save_settings, self.dlg_btn2)) self.dlg_btn2.rejected.connect(partial(self.deactivate_signals)) self.refresh_selection_type() tools_gw.open_dialog(self.dlg_btn2) def selection_type_changed(self, new_type): self.selection_type = SelectionType(new_type) print(f"Selection type changed to { SelectionType(new_type) }") self.refresh_selection_type() def refresh_selection_type(self): if self.selection_type == SelectionType.ACTIVE: self.dlg_btn2.chk_layer_arc.setEnabled(False) self.dlg_btn2.chk_layer_connec.setEnabled(False) self.dlg_btn2.chk_layer_node.setEnabled(False) else: self.dlg_btn2.chk_layer_arc.setEnabled(True) self.dlg_btn2.chk_layer_connec.setEnabled(True) self.dlg_btn2.chk_layer_node.setEnabled(True) def selection_start(self): print(f"Selection state started") self.is_selecting = True self.dlg_btn2.rdb_layers_active.setEnabled(False) self.dlg_btn2.rdb_layers_all.setEnabled(False) # self.dlg_btn2.btn_select.setEnabled(False) # self.emit_point = QgsMapToolEmitPoint(self.canvas) self.canvas.setMapTool(self.emit_point) # Snapper self.snapper_manager = snap_manager.GwSnapManager(self.iface) self.snapper = self.snapper_manager.get_snapper() # Vertex marker self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setColor(QColor(255, 100, 255)) self.vertex_marker.setIconSize(15) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CROSS) self.vertex_marker.setPenWidth(3) # Store user snapping configuration self.previous_snapping = self.snapper_manager.get_snapping_options() if self.selection_type == SelectionType.ACTIVE: print("single selector") self.activate_snapping(self.emit_point) elif self.selection_type == SelectionType.ALL: print("all selector") # Store user snapping configuration if tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_arc) or \ tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_connec) or \ tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_node): self.set_user_config() self.activate_snapping(self.emit_point) def set_user_config(self): print(f"set_user_config") # Disable snapping self.snapper_manager.set_snapping_status() # Set snapping to 'arc', 'node', 'connec' and 'gully' self.snapper_manager.set_snapping_layers() if tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_arc): print("Set snap to arc") self.snapper_manager.config_snap_to_arc() if tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_connec): print("Set snap to connec") self.snapper_manager.config_snap_to_connec() if tools_qt.is_checked(self.dlg_btn2, self.dlg_btn2.chk_layer_node): print("Set snap to node") self.snapper_manager.config_snap_to_node() self.snapper_manager.set_snap_mode() def activate_snapping(self, emit_point): # Set signals self.canvas.xyCoordinates.connect(self.canvas_move_event) emit_point.canvasClicked.connect( partial(self.canvas_release_event, emit_point)) def canvas_move_event(self, point): # Get clicked point self.vertex_marker.hide() event_point = self.snapper_manager.get_event_point(point=point) if self.selection_type == SelectionType.ACTIVE: result = self.snapper_manager.snap_to_current_layer(event_point) elif self.selection_type == SelectionType.ALL: result = self.snapper_manager.snap_to_project_config_layers( event_point) if self.snapper_manager.result_is_valid(): self.snapper_manager.add_marker(result, self.vertex_marker) def canvas_release_event(self, emit_point, point, btn): if btn == Qt.RightButton: if btn == Qt.RightButton: tools_qgis.disconnect_snapping(False, emit_point, self.vertex_marker) return # Get coordinates event_point = self.snapper_manager.get_event_point(point=point) if self.selection_type == SelectionType.ACTIVE: result = self.snapper_manager.snap_to_current_layer(event_point) elif self.selection_type == SelectionType.ALL: result = self.snapper_manager.snap_to_project_config_layers( event_point) if not result.isValid(): return layer = self.snapper_manager.get_snapped_layer(result) # Get the point. Leave selection snapped_feat = self.snapper_manager.get_snapped_feature(result) feature_id = self.snapper_manager.get_snapped_feature_id(result) snapped_point = self.snapper_manager.get_snapped_point(result) layer.select([feature_id]) self.snapper_manager.restore_snap_options(self.previous_snapping) self.deactivate_signals() def deactivate_signals(self): self.vertex_marker.hide() try: self.canvas.xyCoordinates.disconnect() except TypeError: pass try: self.emit_point.canvasClicked.disconnect() except TypeError: pass
class GwDimensioning: def __init__(self): """ Class constructor """ self.iface = global_vars.iface self.settings = global_vars.settings self.controller = global_vars.controller self.plugin_dir = global_vars.plugin_dir self.canvas = global_vars.canvas self.vertex_marker = QgsVertexMarker(self.canvas) def open_dimensioning_form(self, qgis_feature=None, layer=None, db_return=None, fid=None, rubber_band=None): self.dlg_dim = DimensioningUi() load_settings(self.dlg_dim) self.user_current_layer = self.iface.activeLayer() # Set layers dimensions, node and connec self.layer_dimensions = self.controller.get_layer_by_tablename( "v_edit_dimensions") self.layer_node = self.controller.get_layer_by_tablename("v_edit_node") self.layer_connec = self.controller.get_layer_by_tablename( "v_edit_connec") feature = None if qgis_feature is None: features = self.layer_dimensions.getFeatures() for feature in features: if feature['id'] == fid: return feature qgis_feature = feature #qgis_feature = self.get_feature_by_id(self.layer_dimensions, fid, 'id') # when funcion is called from new feature if db_return is None: rubber_band = QgsRubberBand(self.canvas, 0) body = create_body() function_name = 'gw_fct_getdimensioning' json_result = self.controller.get_json(function_name, body) if json_result is None: return False db_return = [json_result] # get id from db response self.fid = db_return[0]['body']['feature']['id'] # ACTION SIGNALS actionSnapping = self.dlg_dim.findChild(QAction, "actionSnapping") actionSnapping.triggered.connect(partial(self.snapping, actionSnapping)) set_icon(actionSnapping, "103") actionOrientation = self.dlg_dim.findChild(QAction, "actionOrientation") actionOrientation.triggered.connect( partial(self.orientation, actionOrientation)) set_icon(actionOrientation, "133") # LAYER SIGNALS self.layer_dimensions.editingStarted.connect( lambda: actionSnapping.setEnabled(True)) self.layer_dimensions.editingStopped.connect( lambda: actionSnapping.setEnabled(False)) self.layer_dimensions.editingStarted.connect( lambda: actionOrientation.setEnabled(True)) self.layer_dimensions.editingStopped.connect( lambda: actionOrientation.setEnabled(False)) self.layer_dimensions.editingStarted.connect( partial(enable_all, self.dlg_dim, db_return[0]['body']['data'])) self.layer_dimensions.editingStopped.connect( partial(disable_all, self.dlg_dim, db_return[0]['body']['data'], False)) # WIDGETS SIGNALS self.dlg_dim.btn_accept.clicked.connect( partial(self.save_dimensioning, qgis_feature, layer)) self.dlg_dim.btn_cancel.clicked.connect( partial(self.cancel_dimensioning)) self.dlg_dim.dlg_closed.connect(partial(self.cancel_dimensioning)) self.dlg_dim.dlg_closed.connect(partial(save_settings, self.dlg_dim)) self.dlg_dim.dlg_closed.connect(rubber_band.reset) self.create_map_tips() layout_list = [] for field in db_return[0]['body']['data']['fields']: if 'hidden' in field and field['hidden']: continue label, widget = self.set_widgets(self.dlg_dim, db_return, field) if widget.objectName() == 'id': qt_tools.setWidgetText(self.dlg_dim, widget, self.fid) layout = self.dlg_dim.findChild(QGridLayout, field['layoutname']) # Profilactic issue to prevent missed layouts againts db response and form if layout is not None: # Take the QGridLayout with the intention of adding a QSpacerItem later if layout not in layout_list and layout.objectName() not in ( 'lyt_top_1', 'lyt_bot_1', 'lyt_bot_2'): layout_list.append(layout) # Add widgets into layout layout.addWidget(label, 0, field['layoutorder']) layout.addWidget(widget, 1, field['layoutorder']) # If field is on top or bottom layout the position is horitzontal no vertical if field['layoutname'] in ('lyt_top_1', 'lyt_bot_1', 'lyt_bot_2'): layout.addWidget(label, 0, field['layoutorder']) layout.addWidget(widget, 1, field['layoutorder']) else: put_widgets(self.dlg_dim, field, label, widget) # Add a QSpacerItem into each QGridLayout of the list for layout in layout_list: vertical_spacer1 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) layout.addItem(vertical_spacer1) if self.layer_dimensions: self.iface.setActiveLayer(self.layer_dimensions) if self.layer_dimensions.isEditable(): actionSnapping.setEnabled(True) actionOrientation.setEnabled(True) enable_all(self.dlg_dim, db_return[0]['body']['data']) else: actionSnapping.setEnabled(False) actionOrientation.setEnabled(False) disable_all(self.dlg_dim, db_return[0]['body']['data'], False) title = f"DIMENSIONING - {self.fid}" open_dialog(self.dlg_dim, dlg_name='dimensioning', title=title) return False, False def cancel_dimensioning(self): self.iface.actionRollbackEdits().trigger() close_dialog(self.dlg_dim) restore_user_layer(self.user_current_layer) def save_dimensioning(self, qgis_feature, layer): # Upsert feature into db layer.updateFeature(qgis_feature) layer.commitChanges() # Create body fields = '' list_widgets = self.dlg_dim.findChildren(QLineEdit) for widget in list_widgets: widget_name = widget.property('columnname') widget_value = qt_tools.getWidgetText(self.dlg_dim, widget) if widget_value == 'null': continue fields += f'"{widget_name}":"{widget_value}", ' list_widgets = self.dlg_dim.findChildren(QCheckBox) for widget in list_widgets: widget_name = widget.property('columnname') widget_value = f'"{qt_tools.isChecked(self.dlg_dim, widget)}"' if widget_value == 'null': continue fields += f'"{widget_name}":{widget_value},' list_widgets = self.dlg_dim.findChildren(QComboBox) for widget in list_widgets: widget_name = widget.property('columnname') widget_value = f'"{qt_tools.get_item_data(self.dlg_dim, widget)}"' if widget_value == 'null': continue fields += f'"{widget_name}":{widget_value},' # remove last character (,) from fields fields = fields[:-1] feature = '"tableName":"v_edit_dimensions", ' feature += f'"id":"{self.fid}"' extras = f'"fields":{{{fields}}}' body = create_body(feature=feature, extras=extras) result = self.controller.get_json('gw_fct_setdimensioning', body) # Close dialog close_dialog(self.dlg_dim) def deactivate_signals(self, action, emit_point=None): self.vertex_marker.hide() try: self.canvas.xyCoordinates.disconnect() except TypeError: pass try: emit_point.canvasClicked.disconnect() except TypeError: pass if not action.isChecked(): action.setChecked(False) return True return False def snapping(self, action): # Set active layer and set signals emit_point = QgsMapToolEmitPoint(self.canvas) self.canvas.setMapTool(emit_point) if self.deactivate_signals(action, emit_point): return remove_marker(self.vertex_marker) self.previous_snapping = get_snapping_options() enable_snapping() snap_to_node() snap_to_connec_gully() set_snapping_mode() self.dlg_dim.actionOrientation.setChecked(False) self.iface.setActiveLayer(self.layer_node) self.canvas.xyCoordinates.connect(self.mouse_move) emit_point.canvasClicked.connect( partial(self.click_button_snapping, action, emit_point)) def mouse_move(self, point): # Hide marker and get coordinates self.vertex_marker.hide() event_point = get_event_point(point=point) # Snapping result = snap_to_background_layers(event_point) if result.isValid(): layer = get_snapped_layer(result) # Check feature if layer == self.layer_node or layer == self.layer_connec: add_marker(result, self.vertex_marker) def click_button_snapping(self, action, emit_point, point, btn): if not self.layer_dimensions: return if btn == Qt.RightButton: if btn == Qt.RightButton: action.setChecked(False) self.deactivate_signals(action, emit_point) return layer = self.layer_dimensions self.iface.setActiveLayer(layer) # Get coordinates event_point = get_event_point(point=point) # Snapping result = snap_to_background_layers(event_point) if result.isValid(): layer = get_snapped_layer(result) # Check feature if layer == self.layer_node: feat_type = 'node' elif layer == self.layer_connec: feat_type = 'connec' else: return # Get the point snapped_feat = get_snapped_feature(result) feature_id = get_snapped_feature_id(result) element_id = snapped_feat.attribute(feat_type + '_id') # Leave selection layer.select([feature_id]) # Get depth of the feature fieldname = None self.project_type = self.controller.get_project_type() if self.project_type == 'ws': fieldname = "depth" elif self.project_type == 'ud' and feat_type == 'node': fieldname = "ymax" elif self.project_type == 'ud' and feat_type == 'connec': fieldname = "connec_depth" if fieldname is None: return depth = snapped_feat.attribute(fieldname) if depth in ('', None, 0, '0', 'NULL'): qt_tools.setText(self.dlg_dim, "depth", None) else: qt_tools.setText(self.dlg_dim, "depth", depth) qt_tools.setText(self.dlg_dim, "feature_id", element_id) qt_tools.setText(self.dlg_dim, "feature_type", feat_type.upper()) apply_snapping_options(self.previous_snapping) self.deactivate_signals(action, emit_point) action.setChecked(False) def orientation(self, action): emit_point = QgsMapToolEmitPoint(self.canvas) self.canvas.setMapTool(emit_point) if self.deactivate_signals(action, emit_point): return remove_marker(self.vertex_marker) self.previous_snapping = get_snapping_options() enable_snapping() snap_to_node() snap_to_connec_gully() set_snapping_mode() self.dlg_dim.actionSnapping.setChecked(False) emit_point.canvasClicked.connect( partial(self.click_button_orientation, action, emit_point)) def click_button_orientation(self, action, emit_point, point, btn): if not self.layer_dimensions: return if btn == Qt.RightButton: action.setChecked(False) self.deactivate_signals(action, emit_point) return self.x_symbol = self.dlg_dim.findChild(QLineEdit, "x_symbol") self.x_symbol.setText(str(int(point.x()))) self.y_symbol = self.dlg_dim.findChild(QLineEdit, "y_symbol") self.y_symbol.setText(str(int(point.y()))) apply_snapping_options(self.previous_snapping) self.deactivate_signals(action, emit_point) action.setChecked(False) def create_map_tips(self): """ Create MapTips on the map """ row = self.controller.get_config('qgis_dim_tooltip') if not row or row[0].lower() != 'true': return self.timer_map_tips = QTimer(self.canvas) self.map_tip_node = QgsMapTip() self.map_tip_connec = QgsMapTip() self.canvas.xyCoordinates.connect(self.map_tip_changed) self.timer_map_tips.timeout.connect(self.show_map_tip) self.timer_map_tips_clear = QTimer(self.canvas) self.timer_map_tips_clear.timeout.connect(self.clear_map_tip) def map_tip_changed(self, point): """ SLOT. Initialize the Timer to show MapTips on the map """ if self.canvas.underMouse(): self.last_map_position = QgsPointXY(point.x(), point.y()) self.map_tip_node.clear(self.canvas) self.map_tip_connec.clear(self.canvas) self.timer_map_tips.start(100) def show_map_tip(self): """ Show MapTips on the map """ self.timer_map_tips.stop() if self.canvas.underMouse(): point_qgs = self.last_map_position point_qt = self.canvas.mouseLastXY() if self.layer_node: self.map_tip_node.showMapTip(self.layer_node, point_qgs, point_qt, self.canvas) if self.layer_connec: self.map_tip_connec.showMapTip(self.layer_connec, point_qgs, point_qt, self.canvas) self.timer_map_tips_clear.start(1000) def clear_map_tip(self): """ Clear MapTips """ self.timer_map_tips_clear.stop() self.map_tip_node.clear(self.canvas) self.map_tip_connec.clear(self.canvas) def set_widgets(self, dialog, db_return, field): widget = None label = None if field['label']: label = QLabel() label.setObjectName('lbl_' + field['widgetname']) label.setText(field['label'].capitalize()) if field['stylesheet'] is not None and 'label' in field[ 'stylesheet']: label = set_setStyleSheet(field, label) if 'tooltip' in field: label.setToolTip(field['tooltip']) else: label.setToolTip(field['label'].capitalize()) if field['widgettype'] == 'text' or field['widgettype'] == 'typeahead': completer = QCompleter() widget = add_lineedit(field) widget = set_widget_size(widget, field) widget = set_data_type(field, widget) if field['widgettype'] == 'typeahead': widget = manage_lineedit(field, dialog, widget, completer) elif field['widgettype'] == 'combo': widget = add_combobox(field) widget = set_widget_size(widget, field) elif field['widgettype'] == 'check': widget = add_checkbox(field) elif field['widgettype'] == 'datetime': widget = add_calendar(dialog, field) elif field['widgettype'] == 'button': widget = add_button(dialog, field) widget = set_widget_size(widget, field) elif field['widgettype'] == 'hyperlink': widget = add_hyperlink(field) widget = set_widget_size(widget, field) elif field['widgettype'] == 'hspacer': widget = add_horizontal_spacer() elif field['widgettype'] == 'vspacer': widget = add_vertical_spacer() elif field['widgettype'] == 'textarea': widget = add_textarea(field) elif field['widgettype'] in 'spinbox': widget = add_spinbox(field) elif field['widgettype'] == 'tableview': widget = add_tableview(db_return, field) widget = set_headers(widget, field) widget = populate_table(widget, field) widget = set_columns_config(widget, field['widgetname'], sort_order=1, isQStandardItemModel=True) qt_tools.set_qtv_config(widget) widget.setObjectName(widget.property('columnname')) return label, widget
class ProfileDockWidget(QDockWidget): """ DockWidget class to display the profile """ closeSignal = pyqtSignal() def __init__(self, iface): """ Constructor :param iface: interface """ QDockWidget.__init__(self) self.setWindowTitle(QCoreApplication.translate("VDLTools", "Profile Tool")) self.resize(1024, 400) self.__iface = iface self.__canvas = self.__iface.mapCanvas() self.__types = ['PDF', 'PNG'] # ], 'SVG', 'PS'] self.__libs = [] if Qwt5_loaded: self.__lib = 'Qwt5' self.__libs.append('Qwt5') if matplotlib_loaded: self.__libs.append('Matplotlib') elif matplotlib_loaded: self.__lib = 'Matplotlib' self.__libs.append('Matplotlib') else: self.__lib = None self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "No graph lib available (qwt5 or matplotlib)"), level=QgsMessageBar.CRITICAL, duration=0) self.__doTracking = False self.__vline = None self.__profiles = None self.__numLines = None self.__mntPoints = None self.__marker = None self.__tabmouseevent = None self.__contentWidget = QWidget() self.setWidget(self.__contentWidget) self.__boxLayout = QHBoxLayout() self.__contentWidget.setLayout(self.__boxLayout) self.__plotFrame = QFrame() self.__frameLayout = QHBoxLayout() self.__plotFrame.setLayout(self.__frameLayout) self.__printLayout = QHBoxLayout() self.__printLayout.addWidget(self.__plotFrame) self.__legendLayout = QVBoxLayout() self.__printLayout.addLayout(self.__legendLayout) self.__printWdg = QWidget() self.__printWdg.setLayout(self.__printLayout) self.__plotWdg = None self.__changePlotWidget() size = QSize(150, 20) self.__boxLayout.addWidget(self.__printWdg) self.__vertLayout = QVBoxLayout() self.__libCombo = QComboBox() self.__libCombo.setFixedSize(size) self.__libCombo.addItems(self.__libs) self.__vertLayout.addWidget(self.__libCombo) self.__libCombo.currentIndexChanged.connect(self.__setLib) self.__maxLabel = QLabel("y max") self.__maxLabel.setFixedSize(size) self.__vertLayout.addWidget(self.__maxLabel) self.__maxSpin = QSpinBox() self.__maxSpin.setFixedSize(size) self.__maxSpin.setRange(-10000, 10000) self.__maxSpin.valueChanged.connect(self.__reScalePlot) self.__vertLayout.addWidget(self.__maxSpin) self.__vertLayout.insertSpacing(10, 20) self.__minLabel = QLabel("y min") self.__minLabel.setFixedSize(size) self.__vertLayout.addWidget(self.__minLabel) self.__minSpin = QSpinBox() self.__minSpin.setFixedSize(size) self.__minSpin.setRange(-10000, 10000) self.__minSpin.valueChanged.connect(self.__reScalePlot) self.__vertLayout.addWidget(self.__minSpin) self.__vertLayout.insertSpacing(10, 40) self.__typeCombo = QComboBox() self.__typeCombo.setFixedSize(size) self.__typeCombo.addItems(self.__types) self.__vertLayout.addWidget(self.__typeCombo) self.__saveButton = QPushButton(QCoreApplication.translate("VDLTools", "Save")) self.__saveButton.setFixedSize(size) self.__saveButton.clicked.connect(self.__save) self.__vertLayout.addWidget(self.__saveButton) self.__boxLayout.addLayout(self.__vertLayout) self.__maxSpin.setEnabled(False) self.__minSpin.setEnabled(False) self.__colors = [] for cn in QColor.colorNames(): qc = QColor(cn) val = qc.red() + qc.green() + qc.blue() if 0 < val < 450: self.__colors.append(cn) def __changePlotWidget(self): """ When plot widget is change (qwt <-> matplotlib) """ self.__activateMouseTracking(False) while self.__frameLayout.count(): child = self.__frameLayout.takeAt(0) child.widget().deleteLater() self.__plotWdg = None if self.__lib == 'Qwt5': self.__plotWdg = QwtPlot(self.__plotFrame) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(10) sizePolicy.setVerticalStretch(0) self.__plotWdg.setSizePolicy(sizePolicy) self.__plotWdg.setAutoFillBackground(False) # Decoration self.__plotWdg.setCanvasBackground(Qt.white) self.__plotWdg.plotLayout().setAlignCanvasToScales(False) self.__plotWdg.plotLayout().setSpacing(100) self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.xBottom) self.__plotWdg.plotLayout().setCanvasMargin(10, QwtPlot.yLeft) title = QwtText(QCoreApplication.translate("VDLTools", "Distance [m]")) title.setFont(QFont("Helvetica", 10)) self.__plotWdg.setAxisTitle(QwtPlot.xBottom, title) title.setText(QCoreApplication.translate("VDLTools", "Elevation [m]")) title.setFont(QFont("Helvetica", 10)) self.__plotWdg.setAxisTitle(QwtPlot.yLeft, title) self.__zoomer = QwtPlotZoomer(QwtPlot.xBottom, QwtPlot.yLeft, QwtPicker.DragSelection, QwtPicker.AlwaysOff, self.__plotWdg.canvas()) self.__zoomer.setRubberBandPen(QPen(Qt.blue)) grid = QwtPlotGrid() grid.setPen(QPen(QColor('grey'), 0, Qt.DotLine)) grid.attach(self.__plotWdg) self.__frameLayout.addWidget(self.__plotWdg) elif self.__lib == 'Matplotlib': # __plotWdg.figure : matplotlib.figure.Figure fig = Figure((1.0, 1.0), linewidth=0.0, subplotpars=SubplotParams(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)) font = {'family': 'arial', 'weight': 'normal', 'size': 12} rc('font', **font) rect = fig.patch rect.set_facecolor((0.9, 0.9, 0.9)) self.__axes = fig.add_axes((0.07, 0.16, 0.92, 0.82)) self.__axes.set_xbound(0, 1000) self.__axes.set_ybound(0, 1000) self.__manageMatplotlibAxe(self.__axes) self.__plotWdg = FigureCanvasQTAgg(fig) sizePolicy = QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) self.__plotWdg.setSizePolicy(sizePolicy) self.__frameLayout.addWidget(self.__plotWdg) def setProfiles(self, profiles, numLines): """ To set the profiles :param profiles: profiles : positions with elevations (for line and points) :param numLines: number of selected connected lines """ self.__numLines = numLines self.__profiles = profiles if self.__lib == 'Matplotlib': self.__prepare_points() def __getLinearPoints(self): """ To extract the linear points of the profile """ profileLen = 0 self.__profiles[0]['l'] = profileLen for i in range(0, len(self.__profiles)-1): x1 = float(self.__profiles[i]['x']) y1 = float(self.__profiles[i]['y']) x2 = float(self.__profiles[i+1]['x']) y2 = float(self.__profiles[i+1]['y']) profileLen += sqrt(((x2-x1)*(x2-x1)) + ((y2-y1)*(y2-y1))) self.__profiles[i+1]['l'] = profileLen def __getMnt(self, settings): """ To get the MN data for the profile :param settings: settings containing MN url """ if settings is None or settings.mntUrl is None or settings.mntUrl == "None": url = 'http://map.lausanne.ch/main/wsgi/profile.json' elif settings.mntUrl == "": return else: url = settings.mntUrl names = ['mnt', 'mns', 'toit_rocher'] url += '?layers=' pos = 0 for name in names: if pos > 0: url += ',' pos += 1 url += name url += '&geom={"type":"LineString","coordinates":[' pos = 0 for i in range(len(self.__profiles)): if pos > 0: url += ',' pos += 1 url += '[' + str(self.__profiles[i]['x']) + ',' + str(self.__profiles[i]['y']) + ']' url = url + ']}&nbPoints=' + str(int(self.__profiles[len(self.__profiles)-1]['l'])) try: response = urlopen(url) j = response.read() j_obj = json.loads(j) profile = j_obj['profile'] self.__mntPoints = [] self.__mntPoints.append(names) mnt_l = [] mnt_z = [] for p in range(len(names)): z = [] mnt_z.append(z) for pt in profile: mnt_l.append(float(pt['dist'])) values = pt['values'] for p in range(len(names)): if names[p] in values: mnt_z[p].append(float(values[names[p]])) else: mnt_z[p].append(None) self.__mntPoints.append(mnt_l) self.__mntPoints.append(mnt_z) except HTTPError as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "HTTP Error"), QCoreApplication.translate("VDLTools", "status error [" + str(e.code) + "] : " + e.reason), level=QgsMessageBar.CRITICAL, duration=0) except URLError as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "URL Error"), e.reason, level=QgsMessageBar.CRITICAL, duration=0) def attachCurves(self, names, settings, usedMnts): """ To attach the curves for the layers to the profile :param names: layers names """ if (self.__profiles is None) or (self.__profiles == 0): return self.__getLinearPoints() if usedMnts is not None and (usedMnts[0] or usedMnts[1] or usedMnts[2]): self.__getMnt(settings) c = 0 if self.__mntPoints is not None: for p in range(len(self.__mntPoints[0])): if usedMnts[p]: legend = QLabel("<font color='" + self.__colors[c] + "'>" + self.__mntPoints[0][p] + "</font>") self.__legendLayout.addWidget(legend) if self.__lib == 'Qwt5': xx = [list(g) for k, g in itertools.groupby(self.__mntPoints[1], lambda x: x is None) if not k] yy = [list(g) for k, g in itertools.groupby(self.__mntPoints[2][p], lambda x: x is None) if not k] for j in range(len(xx)): curve = QwtPlotCurve(self.__mntPoints[0][p]) curve.setData(xx[j], yy[j]) curve.setPen(QPen(QColor(self.__colors[c]), 3)) curve.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': qcol = QColor(self.__colors[c]) self.__plotWdg.figure.get_axes()[0].plot(self.__mntPoints[1], self.__mntPoints[2][p], gid=self.__mntPoints[0][p], linewidth=3) tmp = self.__plotWdg.figure.get_axes()[0].get_lines() for t in range(len(tmp)): if self.__mntPoints[0][p] == tmp[t].get_gid(): tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0), old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0))) self.__plotWdg.draw() break c += 1 if 'z' in self.__profiles[0]: for i in range(len(self.__profiles[0]['z'])): if i < self.__numLines: v = 0 else: v = i - self.__numLines + 1 name = names[v] xx = [] yy = [] for prof in self.__profiles: xx.append(prof['l']) yy.append(prof['z'][i]) for j in range(len(yy)): if yy[j] is None: xx[j] = None if i == 0 or i > (self.__numLines-1): legend = QLabel("<font color='" + self.__colors[c] + "'>" + name + "</font>") self.__legendLayout.addWidget(legend) if self.__lib == 'Qwt5': # Split xx and yy into single lines at None values xx = [list(g) for k, g in itertools.groupby(xx, lambda x: x is None) if not k] yy = [list(g) for k, g in itertools.groupby(yy, lambda x: x is None) if not k] # Create & attach one QwtPlotCurve per one single line for j in range(len(xx)): curve = QwtPlotCurve(name) curve.setData(xx[j], yy[j]) curve.setPen(QPen(QColor(self.__colors[c]), 3)) if i > (self.__numLines-1): curve.setStyle(QwtPlotCurve.Dots) pen = QPen(QColor(self.__colors[c]), 8) pen.setCapStyle(Qt.RoundCap) curve.setPen(pen) curve.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': qcol = QColor(self.__colors[c]) if i < self.__numLines: self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=3) else: self.__plotWdg.figure.get_axes()[0].plot(xx, yy, gid=name, linewidth=5, marker='o', linestyle='None') tmp = self.__plotWdg.figure.get_axes()[0].get_lines() for t in range(len(tmp)): if name == tmp[t].get_gid(): tmp[c].set_color((old_div(qcol.red(), 255.0), old_div(qcol.green(), 255.0), old_div(qcol.blue(), 255.0), old_div(qcol.alpha(), 255.0))) self.__plotWdg.draw() break c += 1 # scaling this try: self.__reScalePlot(None, True) except: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Rescale problem... (trace printed)"), level=QgsMessageBar.CRITICAL, duration=0) print( QCoreApplication.translate("VDLTools", "rescale problem : "), sys.exc_info()[0], traceback.format_exc()) if self.__lib == 'Qwt5': self.__plotWdg.replot() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].redraw_in_frame() self.__plotWdg.draw() self.__activateMouseTracking(True) self.__marker.show() def __reScalePlot(self, value=None, auto=False): """ To rescale the profile plot depending to the bounds """ if (self.__profiles is None) or (self.__profiles == 0): self.__plotWdg.replot() return maxi = 0 for i in range(len(self.__profiles)): if (int(self.__profiles[i]['l'])) > maxi: maxi = int(self.__profiles[i]['l']) + 1 if self.__lib == 'Qwt5': self.__plotWdg.setAxisScale(2, 0, maxi, 0) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].set_xbound(0, maxi) minimumValue = self.__minSpin.value() maximumValue = self.__maxSpin.value() # to set max y and min y displayed if auto: minimumValue = 1000000000 maximumValue = -1000000000 for i in range(len(self.__profiles)): if 'z' in self.__profiles[i]: mini = self.__minTab(self.__profiles[i]['z']) if int(mini) < minimumValue: minimumValue = int(mini) - 1 maxi = self.__maxTab(self.__profiles[i]['z']) if int(maxi) > maximumValue: maximumValue = int(maxi) + 1 if self.__mntPoints is not None: for pts in self.__mntPoints[2]: miniMnt = self.__minTab(pts) if int(miniMnt) < minimumValue: minimumValue = int(miniMnt) - 1 maxiMnt = self.__maxTab(pts) if int(maxiMnt) > maximumValue: maximumValue = int(maxiMnt) + 1 self.__maxSpin.setValue(maximumValue) self.__minSpin.setValue(minimumValue) self.__maxSpin.setEnabled(True) self.__minSpin.setEnabled(True) if self.__lib == 'Qwt5': rect = QRectF(0, minimumValue, maxi, maximumValue-minimumValue) self.__zoomer.setZoomBase(rect) # to draw vertical lines for i in range(len(self.__profiles)): zz = [] for j in range(self.__numLines): if self.__profiles[i]['z'][j] is not None: zz.append(j) color = None if len(zz) == 2: width = 3 color = QColor('red') else: width = 1 if self.__lib == 'Qwt5': vertLine = QwtPlotMarker() vertLine.setLineStyle(QwtPlotMarker.VLine) pen = vertLine.linePen() pen.setWidth(width) if color is not None: pen.setColor(color) vertLine.setLinePen(pen) vertLine.setXValue(self.__profiles[i]['l']) label = vertLine.label() label.setText(str(i)) vertLine.setLabel(label) vertLine.setLabelAlignment(Qt.AlignLeft) vertLine.attach(self.__plotWdg) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].vlines(self.__profiles[i]['l'], minimumValue, maximumValue, linewidth=width) if minimumValue < maximumValue: if self.__lib == 'Qwt5': self.__plotWdg.setAxisScale(0, minimumValue, maximumValue, 0) self.__plotWdg.replot() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].set_ybound(minimumValue, maximumValue) self.__plotWdg.figure.get_axes()[0].redraw_in_frame() self.__plotWdg.draw() @staticmethod def __minTab(tab): """ To get the minimum value in a table :param tab: table to scan :return: minimum value """ mini = 1000000000 for t in tab: if t is None: continue if t < mini: mini = t return mini @staticmethod def __maxTab(tab): """ To get the maximum value in a table :param tab: table to scan :return: maximum value """ maxi = -1000000000 for t in tab: if t is None: continue if t > maxi: maxi = t return maxi def __setLib(self): """ To set the new widget library (qwt <-> matplotlib) """ self.__lib = self.__libs[self.__libCombo.currentIndex()] self.__changePlotWidget() def __save(self): """ To save the profile in a file, on selected format """ idx = self.__typeCombo.currentIndex() if idx == 0: self.__outPDF() elif idx == 1: self.__outPNG() else: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Invalid index ") + str(idx), level=QgsMessageBar.CRITICAL, duration=0) def __outPDF(self): """ To save the profile as pdf file """ fileName = QFileDialog.getSaveFileName( self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"), QCoreApplication.translate("VDLTools", "Profile.pdf"),"Portable Document Format (*.pdf)") if fileName is not None: if self.__lib == 'Qwt5': printer = QPrinter() printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin")) printer.setOutputFileName(fileName) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOrientation(QPrinter.Landscape) self.__plotWdg.print_(printer) elif self.__lib == 'Matplotlib': self.__plotWdg.figure.savefig(str(fileName)) # printer = QPrinter() # printer.setCreator(QCoreApplication.translate("VDLTools", "QGIS Profile Plugin")) # printer.setOutputFileName(fileName) # printer.setOutputFormat(QPrinter.PdfFormat) # printer.setOrientation(QPrinter.Landscape) # printer.setPaperSize(QSizeF(self.__printWdg.size()), QPrinter.Millimeter) # printer.setFullPage(True) # self.__printWdg.render(printer) def __outPNG(self): """ To save the profile as png file """ fileName = QFileDialog.getSaveFileName( self.__iface.mainWindow(), QCoreApplication.translate("VDLTools", "Save As"), QCoreApplication.translate("VDLTools", "Profile.png"),"Portable Network Graphics (*.png)") if fileName is not None: QPixmap.grabWidget(self.__printWdg).save(fileName, "PNG") def clearData(self): """ To clear the displayed data """ if self.__profiles is None: return if self.__lib == 'Qwt5': self.__plotWdg.clear() self.__profiles = None temp1 = self.__plotWdg.itemList() for j in range(len(temp1)): if temp1[j].rtti() == QwtPlotItem.Rtti_PlotCurve: temp1[j].detach() elif self.__lib == 'Matplotlib': self.__plotWdg.figure.get_axes()[0].cla() self.__manageMatplotlibAxe(self.__plotWdg.figure.get_axes()[0]) self.__maxSpin.setEnabled(False) self.__minSpin.setEnabled(False) self.__maxSpin.setValue(0) self.__minSpin.setValue(0) # clear legend while self.__legendLayout.count(): child = self.__legendLayout.takeAt(0) child.widget().deleteLater() def __manageMatplotlibAxe(self, axe): """ To manage the axes for matplotlib library :param axe: the axes element """ axe.grid() axe.tick_params(axis="both", which="major", direction="out", length=10, width=1, bottom=True, top=False, left=True, right=False) axe.minorticks_on() axe.tick_params(axis="both", which="minor", direction="out", length=5, width=1, bottom=True, top=False, left=True, right=False) axe.set_xlabel(QCoreApplication.translate("VDLTools", "Distance [m]")) axe.set_ylabel(QCoreApplication.translate("VDLTools", "Elevation [m]")) def __activateMouseTracking(self, activate): """ To (de)activate the mouse tracking on the profile for matplotlib library :param activate: true to activate, false to deactivate """ if activate: self.__doTracking = True self.__loadRubber() self.cid = self.__plotWdg.mpl_connect('motion_notify_event', self.__mouseevent_mpl) elif self.__doTracking: self.__doTracking = False self.__plotWdg.mpl_disconnect(self.cid) if self.__marker is not None: self.__canvas.scene().removeItem(self.__marker) try: if self.__vline is not None: self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline) self.__plotWdg.draw() except Exception as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Tracking exception : ") + str(e), level=QgsMessageBar.CRITICAL, duration=0) def __mouseevent_mpl(self, event): """ To manage matplotlib mouse tracking event :param event: mouse tracking event """ if event.xdata is not None: try: if self.__vline is not None: self.__plotWdg.figure.get_axes()[0].lines.remove(self.__vline) except Exception as e: self.__iface.messageBar().pushMessage( QCoreApplication.translate("VDLTools", "Mouse event exception : ") + str(e), level=QgsMessageBar.CRITICAL, duration=0) xdata = float(event.xdata) self.__vline = self.__plotWdg.figure.get_axes()[0].axvline(xdata, linewidth=2, color='k') self.__plotWdg.draw() i = 1 while i < len(self.__tabmouseevent)-1 and xdata > self.__tabmouseevent[i][0]: i += 1 i -= 1 x = self.__tabmouseevent[i][1] + (self.__tabmouseevent[i + 1][1] - self.__tabmouseevent[i][1]) / ( self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0]) y = self.__tabmouseevent[i][2] + (self.__tabmouseevent[i + 1][2] - self.__tabmouseevent[i][2]) / ( self.__tabmouseevent[i + 1][0] - self.__tabmouseevent[i][0]) * (xdata - self.__tabmouseevent[i][0]) self.__marker.show() self.__marker.setCenter(QgsPoint(x, y)) def __loadRubber(self): """ To load te rubber band for mouse tracking on map """ self.__marker = QgsVertexMarker(self.__canvas) self.__marker.setIconSize(5) self.__marker.setIconType(QgsVertexMarker.ICON_BOX) self.__marker.setPenWidth(3) def __prepare_points(self): """ To prepare the points on map for mouse tracking on profile """ self.__tabmouseevent = [] length = 0 for i, point in enumerate(self.__profiles): if i == 0: self.__tabmouseevent.append([0, point['x'], point['y']]) else: length += ((self.__profiles[i]['x'] - self.__profiles[i-1]['x']) ** 2 + (self.__profiles[i]['y'] - self.__profiles[i-1]['y']) ** 2) ** 0.5 self.__tabmouseevent.append([float(length), float(point['x']), float(point['y'])]) def closeEvent(self, event): """ When the dock widget is closed :param event: close event """ if self.__maxSpin is not None: Signal.safelyDisconnect(self.__maxSpin.valueChanged, self.__reScalePlot) self.__maxSpin = None if self.__minSpin is not None: Signal.safelyDisconnect(self.__minSpin.valueChanged, self.__reScalePlot) self.__minSpin = None if self.__saveButton is not None: Signal.safelyDisconnect(self.__saveButton.clicked, self.__save) self.__saveButton = None if self.__libCombo is not None: Signal.safelyDisconnect(self.__libCombo.currentIndexChanged, self.__setLib) self.__libCombo = None self.closeSignal.emit() if self.__marker is not None: self.__marker.hide() QDockWidget.closeEvent(self, event)
class MoveNodeMapTool(ParentMapTool): ''' Button 16. Move node Execute SQL function: 'gw_fct_node2arc' ''' def __init__(self, iface, settings, action, index_action, controller, srid): ''' Class constructor ''' # Call ParentMapTool constructor super(MoveNodeMapTool, self).__init__(iface, settings, action, index_action) self.srid = srid # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setIconSize(9) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Rubber band self.rubberBand = QgsRubberBand(self.canvas, QGis.Line) mFillColor = QColor(255, 0, 0); self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(3) self.reset() def reset(self): # Clear selected features layer = self.canvas.currentLayer() if layer is not None: layer.removeSelection() # Graphic elements self.rubberBand.reset() def move_node(self, node_id, point): ''' Move selected node to the current point ''' if self.srid is None: self.srid = self.settings.value('db/srid') if self.schema_name is None: self.schema_name = self.settings.value('db/schema_name') # Update node geometry the_geom = "ST_GeomFromText('POINT("+str(point.x())+" "+str(point.y())+")', "+str(self.srid)+")"; sql = "UPDATE "+self.schema_name+".node SET the_geom = "+the_geom sql+= " WHERE node_id = '"+node_id+"'" status = self.controller.execute_sql(sql) if status: # Execute SQL function and show result to the user function_name = "gw_fct_node2arc" sql = "SELECT "+self.schema_name+"."+function_name+"('"+str(node_id)+"');" self.controller.execute_sql(sql) else: message = "Move node: Error updating geometry" self.controller.show_warning(message, context_name='ui_message') # Refresh map canvas self.canvas.currentLayer().triggerRepaint() ''' QgsMapTool inherited event functions ''' def activate(self): ''' Called when set as currently active map tool ''' # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to node self.snapperManager.snapToNode() self.snapperManager.snapToArc() # Change pointer cursor = QCursor() cursor.setShape(Qt.CrossCursor) # Get default cursor self.stdCursor = self.parent().cursor() # And finally we set the mapTool's parent cursor self.parent().setCursor(cursor) # Reset self.reset() # Show help message when action is activated if self.show_help: message = "Select the disconnected node by clicking on it, move the pointer to desired location inside a pipe and click again" self.controller.show_info(message, context_name='ui_message' ) # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_node) except: self.canvas.setCurrentLayer(self.layer_node) def deactivate(self): ''' Called when map tool is being deactivated ''' # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) try: self.rubberBand.reset(QGis.Line) except AttributeError: pass def canvasMoveEvent(self, event): ''' Mouse movement event ''' # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() if layer is None: return # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(0, 255, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Set a new point to go on with #self.appendPoint(point) self.rubberBand.movePoint(point) else: point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) else: # Snap to arc result = [] (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()) and (result[0].snappedVertexNr == -1): point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setColor(QColor(255, 0, 0)) self.vertexMarker.setCenter(point) self.vertexMarker.show() # Select the arc self.layer_arc.removeSelection() self.layer_arc.select([result[0].snappedAtGeometry]) # Bring the rubberband to the cursor i.e. the clicked point self.rubberBand.movePoint(point) else: # Bring the rubberband to the cursor i.e. the clicked point point = QgsMapToPixel.toMapCoordinates(self.canvas.getCoordinateTransform(), x, y) self.rubberBand.movePoint(point) def canvasReleaseEvent(self, event): ''' Mouse release event ''' if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x,y) # Node layer layer = self.canvas.currentLayer() # Select node or arc if layer.selectedFeatureCount() == 0: # Snap to node (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_node.name()): point = QgsPoint(result[0].snappedVertex) layer.select([result[0].snappedAtGeometry]) # Hide highlight self.vertexMarker.hide() # Set a new point to go on with self.rubberBand.addPoint(point) else: # Snap to arc (retval,result) = self.snapper.snapToBackgroundLayers(eventPoint) #@UnusedVariable # That's the snapped point if (result <> []) and (result[0].layer.name() == self.layer_arc.name()): point = QgsPoint(result[0].snappedVertex) # Get selected feature (at this moment it will have one and only one) feature = layer.selectedFeatures()[0] node_id = feature.attribute('node_id') # Move selected node to the released point self.move_node(node_id, point) # Rubberband reset self.reset() # Refresh map canvas self.iface.mapCanvas().refresh() elif event.button() == Qt.RightButton: self.reset()
class QGISRedCreatePipeTool(QgsMapTool): def __init__(self, button, iface, projectDirectory, netwName, parent): QgsMapTool.__init__(self, iface.mapCanvas()) self.iface = iface self.ProjectDirectory = projectDirectory self.NetworkName = netwName self.parent = parent self.setAction(button) self.startMarker = QgsVertexMarker(self.iface.mapCanvas()) self.startMarker.setColor(QColor(255, 87, 51)) self.startMarker.setIconSize(15) self.startMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.startMarker.setPenWidth(3) self.startMarker.hide() self.endMarker = QgsVertexMarker(self.iface.mapCanvas()) self.endMarker.setColor(QColor(255, 87, 51)) self.endMarker.setIconSize(15) self.endMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.endMarker.setPenWidth(3) self.endMarker.hide() self.snapper = None self.rubberBand1 = None self.rubberBand2 = None self.resetProperties() def activate(self): QgsMapTool.activate(self) # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(1) # Vertex config.setMode(2) # All layers config.setTolerance(2) config.setUnits(2) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def deactivate(self): self.resetProperties() QgsMapTool.deactivate(self) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def resetProperties(self): # self.toolbarButton.setChecked(False) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.startMarker.hide() self.endMarker.hide() self.mousePoints = [] self.firstClicked = False self.objectSnapped = None self.rubberBand1 = None self.rubberBand2 = None def createRubberBand(self, points): myPoints1 = [] for p in points: myPoints1.append(QgsPoint(p.x(), p.y())) myPoints1.remove(myPoints1[-1]) if self.rubberBand1 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand1) self.rubberBand1 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand1.setToGeometry(QgsGeometry.fromPolyline(myPoints1), None) self.rubberBand1.setColor(QColor(240, 40, 40)) self.rubberBand1.setWidth(1) self.rubberBand1.setLineStyle(Qt.SolidLine) myPoints2 = [] myPoints2.append(QgsPoint(points[-2].x(), points[-2].y())) myPoints2.append(QgsPoint(points[-1].x(), points[-1].y())) if self.rubberBand2 is not None: self.iface.mapCanvas().scene().removeItem(self.rubberBand2) self.rubberBand2 = QgsRubberBand(self.iface.mapCanvas(), False) self.rubberBand2.setToGeometry(QgsGeometry.fromPolyline(myPoints2), None) self.rubberBand2.setColor(QColor(240, 40, 40)) self.rubberBand2.setWidth(1) self.rubberBand2.setLineStyle(Qt.DashLine) """Events""" def canvasPressEvent(self, event): if event.button() == Qt.LeftButton: if not self.firstClicked: self.firstClicked = True point = self.toMapCoordinates(event.pos()) if self.objectSnapped is not None: point = self.objectSnapped.point() self.mousePoints.append(point) self.mousePoints.append(point) else: self.mousePoints.append(self.mousePoints[-1]) self.createRubberBand(self.mousePoints) if event.button() == Qt.RightButton: self.mousePoints.remove(self.mousePoints[-1]) if self.firstClicked: if (len(self.mousePoints) == 2 and self.mousePoints[0] == self.mousePoints[1]): createdPipe = False elif len(self.mousePoints) < 2: createdPipe = False else: createdPipe = True if createdPipe: self.parent.runCreatePipe(self.mousePoints) self.resetProperties() def canvasMoveEvent(self, event): # Mouse not clicked if not self.firstClicked: match = self.snapper.snapToMap(self.toMapCoordinates(event.pos())) if match.isValid(): self.objectSnapped = match self.startMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.startMarker.show() else: self.objectSnapped = None self.startMarker.hide() # Mouse clicked else: point = self.toMapCoordinates(event.pos()) match = self.snapper.snapToMap(point) if match.isValid(): self.objectSnapped = match self.endMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.endMarker.show() self.mousePoints[-1] = match.point() else: self.objectSnapped = None self.endMarker.hide() self.mousePoints[-1] = point self.createRubberBand(self.mousePoints)
class DeleteNodeMapTool(ParentMapTool): ''' Button 17. User select one node. Execute SQL function: 'gw_fct_delete_node' ''' def __init__(self, iface, settings, action, index_action): ''' Class constructor ''' # Call ParentMapTool constructor super(DeleteNodeMapTool, self).__init__(iface, settings, action, index_action) # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(12) self.vertexMarker.setIconType(QgsVertexMarker.ICON_CIRCLE) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) ''' QgsMapTools inherited event functions ''' def canvasMoveEvent(self, event): # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name(): # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() break def canvasReleaseEvent(self, event): # With left click the digitizing is finished if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) snappFeat = None # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer.name() == self.layer_node.name(): # Get the point point = QgsPoint(result[0].snappedVertex) snappFeat = next( result[0].layer.getFeatures(QgsFeatureRequest().setFilterFid(result[0].snappedAtGeometry))) break if snappFeat is not None: # Get selected features and layer type: 'node' feature = snappFeat node_id = feature.attribute('node_id') # Execute SQL function and show result to the user function_name = "gw_fct_delete_node" sql = "SELECT " + self.schema_name + "." + function_name + "('" + str(node_id) + "');" status = self.controller.execute_sql(sql) if status: message = "Node deleted successfully" self.controller.show_warning(message, context_name='ui_message' ) # Refresh map canvas self.iface.mapCanvas().refresh() def activate(self): # Check button self.action().setChecked(True) # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to node self.snapperManager.snapToNode() # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = "Select the node inside a pipe by clicking on it and it will be removed" self.controller.show_warning(message, context_name='ui_message') # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_node) except: self.canvas.setCurrentLayer(self.layer_node) def deactivate(self): # Check button self.action().setChecked(False) # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) # Removehighlight self.h = None
class PointTool(QgsMapToolEdit): ''' Implementation of interactions of the user with the main map. Will called every time the user clicks on the map or hovers the mouse over the map. ''' def deactivate(self): QgsMapTool.deactivate(self) self.deactivated.emit() def __init__(self, canvas, iface, turn_off_snap, smooth=False): ''' canvas - link to the QgsCanvas of the application iface - link to the Qgis Interface turn_off_snap - flag sets snapping to the nearest color smooth - flag sets smoothing of the traced path ''' self.iface = iface # list of Anchors for current line self.anchors = [] # for keeping track of mouse event for rubber band updating self.last_mouse_event_pos = None self.tracing_mode = TracingModes.PATH self.turn_off_snap = turn_off_snap self.smooth_line = smooth # possible variants: gray_diff, as_is, color_diff (using v from hsv) self.grid_conversion = "gray_diff" # QApplication.restoreOverrideCursor() # QApplication.setOverrideCursor(Qt.CrossCursor) QgsMapToolEmitPoint.__init__(self, canvas) self.rlayer = None self.grid_changed = None self.snap_tolerance = None self.vlayer = None self.grid = None self.sample = None self.tracking_is_active = False # False = not a polygon self.rubber_band = QgsRubberBand(self.canvas(), False) self.markers = [] self.marker_snap = QgsVertexMarker(self.canvas()) self.marker_snap.setColor(QColor(255, 0, 255)) self.find_path_task = None self.change_state(WaitingFirstPointState) def display_message( self, title, message, level='Info', duration=2, ): ''' Shows message bar to the user. `level` receives one of four possible string values: Info, Warning, Critical, Success ''' LEVELS = { 'Info': Qgis.Info, 'Warning': Qgis.Warning, 'Critical': Qgis.Critical, 'Success': Qgis.Success, } self.iface.messageBar().pushMessage(title, message, LEVELS[level], duration) def change_state(self, state): self.state = state(self) def snap_tolerance_changed(self, snap_tolerance): self.snap_tolerance = snap_tolerance if snap_tolerance is None: self.marker_snap.hide() else: self.marker_snap.show() def trace_color_changed(self, color): r, g, b = self.sample if color is False: self.grid_changed = None else: r0, g0, b0, t = color.getRgb() self.grid_changed = np.abs((r0 - r)**2 + (g0 - g)**2 + (b0 - b)**2) def get_current_vector_layer(self): try: vlayer = self.iface.layerTreeView().selectedLayers()[0] if isinstance(vlayer, QgsVectorLayer): if vlayer.wkbType() == QgsWkbTypes.MultiLineString: return vlayer else: self.display_message( " ", "The active layer must be" + " a MultiLineString vector layer", level='Warning', duration=2, ) return None else: self.display_message( "Missing Layer", "Please select vector layer to draw", level='Warning', duration=2, ) return None except IndexError: self.display_message( "Missing Layer", "Please select vector layer to draw", level='Warning', duration=2, ) return None def raster_layer_has_changed(self, raster_layer): self.rlayer = raster_layer if self.rlayer is None: self.display_message( "Missing Layer", "Please select raster layer to trace", level='Warning', duration=2, ) return try: sample, to_indexes, to_coords, to_coords_provider, \ to_coords_provider2 = \ get_whole_raster(self.rlayer, QgsProject.instance(), ) except PossiblyIndexedImageError: self.display_message( "Missing Layer", "Can't trace indexed or gray image", level='Critical', duration=2, ) return r = sample[0].astype(float) g = sample[1].astype(float) b = sample[2].astype(float) where_are_NaNs = np.isnan(r) r[where_are_NaNs] = 0 where_are_NaNs = np.isnan(g) g[where_are_NaNs] = 0 where_are_NaNs = np.isnan(b) b[where_are_NaNs] = 0 self.sample = (r, g, b) self.grid = r + g + b self.to_indexes = to_indexes self.to_coords = to_coords self.to_coords_provider = to_coords_provider self.to_coords_provider2 = to_coords_provider2 def remove_last_anchor_point(self, undo_edit=True, redraw=True): ''' Removes last anchor point and last marker point ''' # check if we have at least one feature to delete vlayer = self.get_current_vector_layer() if vlayer is None: return if vlayer.featureCount() < 1: return # remove last marker if self.markers: last_marker = self.markers.pop() self.canvas().scene().removeItem(last_marker) # remove last anchor if self.anchors: self.anchors.pop() if undo_edit: # it's a very ugly way of triggering single undo event self.iface.editMenu().actions()[0].trigger() if redraw: self.update_rubber_band() self.redraw() def keyPressEvent(self, e): if e.key() == Qt.Key_Backspace or e.key() == Qt.Key_B: # delete last segment if backspace is pressed self.remove_last_anchor_point() elif e.key() == Qt.Key_A: # change tracing mode self.tracing_mode = self.tracing_mode.next() self.update_rubber_band() elif e.key() == Qt.Key_S: # toggle snap mode self.turn_off_snap() elif e.key() == Qt.Key_Escape: # Abort tracing process self.abort_tracing_process() def add_anchor_points(self, x1, y1, i1, j1): ''' Adds anchor points and markers to self. ''' anchor = Anchor(x1, y1, i1, j1) self.anchors.append(anchor) marker = QgsVertexMarker(self.canvas()) marker.setCenter(QgsPointXY(x1, y1)) self.markers.append(marker) def trace_over_image(self, start, goal, do_it_as_task=False, vlayer=None): ''' performs tracing ''' i0, j0 = start i1, j1 = goal r, g, b, = self.sample try: r0 = r[i1, j1] g0 = g[i1, j1] b0 = b[i1, j1] except IndexError: raise OutsideMapError if self.grid_changed is None: grid = np.abs((r0 - r)**2 + (g0 - g)**2 + (b0 - b)**2) else: grid = self.grid_changed if do_it_as_task: # dirty hack to avoid QGIS crashing self.find_path_task = FindPathTask( grid.astype(np.dtype('l')), start, goal, self.draw_path, vlayer, ) QgsApplication.taskManager().addTask(self.find_path_task, ) self.tracking_is_active = True else: path, cost = FindPathFunction( grid.astype(np.dtype('l')), (i0, j0), (i1, j1), ) return path, cost def trace(self, x1, y1, i1, j1, vlayer): ''' Traces path from last point to given point. In case tracing is inactive just creates straight line. ''' if self.tracing_mode.is_tracing(): if self.snap_tolerance is not None: try: i1, j1 = self.snap(i1, j1) except OutsideMapError: return _, _, i0, j0 = self.anchors[-2] start_point = i0, j0 end_point = i1, j1 try: self.trace_over_image(start_point, end_point, do_it_as_task=True, vlayer=vlayer) except OutsideMapError: pass else: self.draw_path( None, vlayer, was_tracing=False, x1=x1, y1=y1, ) def snap(self, i, j): if self.snap_tolerance is None: return i, j if not self.tracing_mode.is_tracing(): return i, j if self.grid_changed is None: return i, j size_i, size_j = self.grid.shape size = self.snap_tolerance if i < size or j < size or i + size > size_i or j + size > size_j: raise OutsideMapError grid_small = self.grid_changed grid_small = grid_small[i - size:i + size, j - size:j + size] smallest_cells = np.where(grid_small == np.amin(grid_small)) coordinates = list(zip(smallest_cells[0], smallest_cells[1])) if len(coordinates) == 1: delta_i, delta_j = coordinates[0] delta_i -= size delta_j -= size else: # find the closest to the center deltas = [(i - size, j - size) for i, j in coordinates] lengths = [(i**2 + j**2) for i, j in deltas] i = lengths.index(min(lengths)) delta_i, delta_j = deltas[i] return i + delta_i, j + delta_j def canvasReleaseEvent(self, mouseEvent): ''' Method where the actual tracing is performed after the user clicked on the map ''' vlayer = self.get_current_vector_layer() if vlayer is None: return if not vlayer.isEditable(): self.display_message( "Edit mode", "Please begin editing vector layer to trace", level='Warning', duration=2, ) return if self.rlayer is None: self.display_message( "Missing Layer", "Please select raster layer to trace", level='Warning', duration=2, ) return if mouseEvent.button() == Qt.RightButton: self.state.click_rmb(mouseEvent, vlayer) elif mouseEvent.button() == Qt.LeftButton: self.state.click_lmb(mouseEvent, vlayer) return def draw_path(self, path, vlayer, was_tracing=True,\ x1=None, y1=None): ''' Draws a path after tracer found it. ''' if was_tracing: if self.smooth_line: path = smooth(path, size=5) path = simplify(path) current_last_point = self.to_coords(*path[-1]) path_ref = [self.to_coords_provider(i, j) for i, j in path] else: x0, y0, _, _ = self.anchors[-2] path_ref = [ self.to_coords_provider2(x0, y0), self.to_coords_provider2(x1, y1) ] current_last_point = (x1, y1) self.ready = False if len(self.anchors) == 2: vlayer.beginEditCommand("Adding new line") add_feature_to_vlayer(vlayer, path_ref) vlayer.endEditCommand() else: x0, y0, _, _ = self.anchors[-2] last_point = self.to_coords_provider2(x0, y0) path_ref = [last_point] + path_ref[1:] vlayer.beginEditCommand("Adding new segment to the line") add_to_last_feature(vlayer, path_ref) vlayer.endEditCommand() _, _, current_last_point_i, current_last_point_j = self.anchors[-1] self.anchors[-1] = current_last_point[0], current_last_point[ 1], current_last_point_i, current_last_point_j self.redraw() self.tracking_is_active = False def update_rubber_band(self): # this is very ugly but I can't make another way if self.last_mouse_event_pos is None: return if not self.anchors: return x0, y0, _, _ = self.anchors[-1] qgsPoint = self.toMapCoordinates(self.last_mouse_event_pos) x1, y1 = qgsPoint.x(), qgsPoint.y() points = [QgsPoint(x0, y0), QgsPoint(x1, y1)] self.rubber_band.setColor(QColor(255, 0, 0)) self.rubber_band.setWidth(3) self.rubber_band.setLineStyle( RUBBERBAND_LINE_STYLES[self.tracing_mode], ) vlayer = self.get_current_vector_layer() if vlayer is None: return self.rubber_band.setToGeometry( QgsGeometry.fromPolyline(points), self.vlayer, ) def canvasMoveEvent(self, mouseEvent): ''' Store the mouse position for the correct updating of the rubber band ''' # we need at least one point to draw if not self.anchors: return if self.snap_tolerance is not None and self.tracing_mode.is_tracing(): qgsPoint = self.toMapCoordinates(mouseEvent.pos()) x1, y1 = qgsPoint.x(), qgsPoint.y() # i, j = get_indxs_from_raster_coords(self.geo_ref, x1, y1) i, j = self.to_indexes(x1, y1) try: i1, j1 = self.snap(i, j) except OutsideMapError: return # x1, y1 = get_coords_from_raster_indxs(self.geo_ref, i1, j1) x1, y1 = self.to_coords(i1, j1) self.marker_snap.setCenter(QgsPointXY(x1, y1)) self.last_mouse_event_pos = mouseEvent.pos() self.update_rubber_band() self.redraw() def abort_tracing_process(self): ''' Terminate background process of tracing raster after the user hits Esc. ''' # check if we have any tasks if self.find_path_task is None: return self.tracking_is_active = False try: # send terminate signal to the task self.find_path_task.cancel() self.find_path_task = None except RuntimeError: return else: self.remove_last_anchor_point(undo_edit=False, ) def redraw(self): # If caching is enabled, a simple canvas refresh might not be # sufficient to trigger a redraw and you must clear the cached image # for the layer if self.iface.mapCanvas().isCachingEnabled(): vlayer = self.get_current_vector_layer() if vlayer is None: return vlayer.triggerRepaint() self.iface.mapCanvas().refresh() QgsApplication.processEvents() def pan(self, x, y): ''' Move the canvas to the x, y position ''' currExt = self.iface.mapCanvas().extent() canvasCenter = currExt.center() dx = x - canvasCenter.x() dy = y - canvasCenter.y() xMin = currExt.xMinimum() + dx xMax = currExt.xMaximum() + dx yMin = currExt.yMinimum() + dy yMax = currExt.yMaximum() + dy newRect = QgsRectangle(xMin, yMin, xMax, yMax) self.iface.mapCanvas().setExtent(newRect)
class captureGPSFeatures(FieldRestrictionTypeUtilsMixin): def __init__(self, iface, featuresWithGPSToolbar): TOMsMessageLog.logMessage("In captureGPSFeatures::init", level=Qgis.Info) FieldRestrictionTypeUtilsMixin.__init__(self, iface) # Save reference to the QGIS interface self.iface = iface self.canvas = self.iface.mapCanvas() self.featuresWithGPSToolbar = featuresWithGPSToolbar self.gpsMapTool = False self.marker = None # This will set up the items on the toolbar # Create actions self.gnssToolGroup = QActionGroup(featuresWithGPSToolbar) self.actionCreateRestriction = QAction(QIcon(":/plugins/featureswithgps/resources/mActionAddTrack.svg"), QCoreApplication.translate("MyPlugin", "Create Restriction"), self.iface.mainWindow()) self.actionCreateRestriction.setCheckable(True) self.actionAddGPSLocation = QAction(QIcon(":/plugins/featureswithgps/resources/greendot3.png"), QCoreApplication.translate("MyPlugin", "Add vertex from gnss"), self.iface.mainWindow()) #self.actionAddGPSLocation.setCheckable(True) self.actionRemoveRestriction = QAction(QIcon(":plugins/featureswithgps/resources/mActionDeleteTrack.svg"), QCoreApplication.translate("MyPlugin", "Remove Restriction"), self.iface.mainWindow()) self.actionRemoveRestriction.setCheckable(True) self.actionRestrictionDetails = QAction(QIcon(":/plugins/featureswithgps/resources/mActionGetInfo.svg"), QCoreApplication.translate("MyPlugin", "Get Restriction Details"), self.iface.mainWindow()) self.actionRestrictionDetails.setCheckable(True) self.gnssToolGroup.addAction(self.actionRestrictionDetails) self.actionCreateSign = QAction(QIcon(":/plugins/featureswithgps/resources/mActionSetEndPoint.svg"), QCoreApplication.translate("MyPlugin", "Create sign from gnss"), self.iface.mainWindow()) self.actionCreateSign.setCheckable(True) self.actionCreateMTR = QAction(QIcon(":/plugins/featureswithgps/resources/UK_traffic_sign_606F.svg"), QCoreApplication.translate("MyPlugin", "Create moving traffic restriction"), self.iface.mainWindow()) self.actionCreateMTR.setCheckable(True) self.actionMoveFeatureToDifferentLayer = QAction(QIcon(""), QCoreApplication.translate("MyPlugin", "Move feature to different layer"), self.iface.mainWindow()) self.actionMoveFeatureToDifferentLayer.setCheckable(True) self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer) # Add actions to the toolbar self.featuresWithGPSToolbar.addAction(self.actionCreateRestriction) self.featuresWithGPSToolbar.addAction(self.actionAddGPSLocation) self.featuresWithGPSToolbar.addAction(self.actionRestrictionDetails) #self.featuresWithGPSToolbar.addAction(self.actionRemoveRestriction) self.featuresWithGPSToolbar.addAction(self.actionCreateSign) #self.featuresWithGPSToolbar.addAction(self.actionCreateMTR) self.featuresWithGPSToolbar.addAction(self.actionMoveFeatureToDifferentLayer) self.gnssToolGroup.addAction(self.actionCreateRestriction) #self.gnssToolGroup.addAction(self.actionAddGPSLocation) #self.gnssToolGroup.addAction(self.actionRemoveRestriction) self.gnssToolGroup.addAction(self.actionRestrictionDetails) #self.gnssToolGroup.addAction(self.actionCreateSign) #self.gnssToolGroup.addAction(self.actionCreateMTR) self.gnssToolGroup.addAction(self.actionMoveFeatureToDifferentLayer) self.gnssToolGroup.setExclusive(True) self.gnssToolGroup.triggered.connect(self.onGroupTriggered) # Connect action signals to slots self.actionCreateRestriction.triggered.connect(self.doCreateRestriction) self.actionAddGPSLocation.triggered.connect(self.doAddGPSLocation) self.actionRestrictionDetails.triggered.connect(self.doRestrictionDetails) #self.actionRemoveRestriction.triggered.connect(self.doRemoveRestriction) self.actionCreateSign.triggered.connect(self.doCreateSign) #self.actionCreateMTR.triggered.connect(self.doCreateMTR) self.actionMoveFeatureToDifferentLayer.triggered.connect(self.doMoveFeatureToDifferentLayer) self.actionCreateRestriction.setEnabled(False) self.actionAddGPSLocation.setEnabled(False) self.actionRestrictionDetails.setEnabled(False) #self.actionRemoveRestriction.setEnabled(False) self.actionCreateSign.setEnabled(False) #self.actionCreateMTR.setEnabled(False) self.actionMoveFeatureToDifferentLayer.setEnabled(False) self.searchBar = searchBar(self.iface, self.featuresWithGPSToolbar) self.searchBar.disableSearchBar() self.mapTool = None self.currGnssAction = None self.gpsConnection = None self.createMapToolDict = {} def enableFeaturesWithGPSToolbarItems(self): TOMsMessageLog.logMessage("In enablefeaturesWithGPSToolbarItems", level=Qgis.Warning) self.gpsAvailable = False self.closeTOMs = False self.tableNames = TOMsLayers(self.iface) self.params = gpsParams() self.tableNames.TOMsLayersNotFound.connect(self.setCloseTOMsFlag) #self.tableNames.gpsLayersNotFound.connect(self.setCloseCaptureGPSFeaturesFlag) self.params.TOMsParamsNotFound.connect(self.setCloseCaptureGPSFeaturesFlag) self.TOMsConfigFileObject = TOMsConfigFile() self.TOMsConfigFileObject.TOMsConfigFileNotFound.connect(self.setCloseTOMsFlag) self.TOMsConfigFileObject.initialiseTOMsConfigFile() self.tableNames.getLayers(self.TOMsConfigFileObject) self.prj = QgsProject().instance() self.dest_crs = self.prj.crs() TOMsMessageLog.logMessage("In captureGPSFeatures::init project CRS is " + self.dest_crs.description(), level=Qgis.Warning) self.transformation = QgsCoordinateTransform(QgsCoordinateReferenceSystem("EPSG:4326"), self.dest_crs, self.prj) self.params.getParams() if self.closeTOMs: QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Unable to start editing tool ...")) #self.actionProposalsPanel.setChecked(False) return # TODO: allow function to continue without GPS enabled ... # Now check to see if the port is set. If not assume that just normal tools gpsPort = self.params.setParam("gpsPort") TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems: GPS port is: {}".format(gpsPort), level=Qgis.Warning) self.gpsConnection = None if gpsPort: self.gpsAvailable = True if self.gpsAvailable == True: self.curr_gps_location = None self.curr_gps_info = None TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - GPS port is specified ", level=Qgis.Info) self.gps_thread = GPS_Thread(self.dest_crs, gpsPort) thread = QThread() self.gps_thread.moveToThread(thread) self.gps_thread.gpsActivated.connect(self.gpsStarted) self.gps_thread.gpsPosition.connect(self.gpsPositionProvided) self.gps_thread.gpsDeactivated.connect(functools.partial(self.gpsStopped)) self.gps_thread.gpsError.connect(self.gpsErrorEncountered) #self.gps_thread.progress.connect(progressBar.setValue) thread.started.connect(self.gps_thread.startGPS) #thread.finished.connect(functools.partial(self.gpsStopped, thread)) thread.start() self.thread = thread TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems - attempting connection ", level=Qgis.Info) time.sleep(1.0) try: self.roamDistance = float(self.params.setParam("roamDistance")) except Exception as e: TOMsMessageLog.logMessage("In enableFeaturesWithGPSToolbarItems:init: roamDistance issue: {}".format(e), level=Qgis.Warning) self.roamDistance = 5.0 self.enableToolbarItems() self.createMapToolDict = {} def enableToolbarItems(self): TOMsMessageLog.logMessage("In enableToolbarItems", level=Qgis.Warning) self.actionCreateRestriction.setEnabled(True) self.actionRestrictionDetails.setEnabled(True) #self.actionRemoveRestriction.setEnabled(True) #self.actionCreateSign.setEnabled(True) #self.actionCreateMTR.setEnabled(True) self.actionMoveFeatureToDifferentLayer.setEnabled(True) self.searchBar.enableSearchBar() self.currMapTool = None self.theCurrentMapTool = None self.iface.currentLayerChanged.connect(self.changeCurrLayer2) self.canvas.mapToolSet.connect(self.changeMapTool2) self.canvas.extentsChanged.connect(self.changeExtents) # transaction for move ... self.localTransaction = MoveLayerTransaction(self.iface) def enableGnssToolbarItem(self): if self.gpsConnection: self.actionAddGPSLocation.setEnabled(True) self.actionCreateSign.setEnabled(True) self.lastCentre = QgsPointXY(0,0) def disableGnssToolbarItem(self): self.actionAddGPSLocation.setEnabled(False) self.actionCreateSign.setEnabled(False) def disableToolbarItems(self): self.actionCreateRestriction.setEnabled(False) self.actionRestrictionDetails.setEnabled(False) self.actionRemoveRestriction.setEnabled(False) self.actionCreateSign.setEnabled(False) #self.actionCreateMTR.setEnabled(False) self.actionMoveFeatureToDifferentLayer.setEnabled(False) self.searchBar.disableSearchBar() """if self.gpsConnection: self.actionAddGPSLocation.setEnabled(False)""" def setCloseTOMsFlag(self): self.closeTOMs = True QMessageBox.information(self.iface.mainWindow(), "ERROR", ("Now closing TOMs ...")) def disableFeaturesWithGPSToolbarItems(self): TOMsMessageLog.logMessage("In disablefeaturesWithGPSToolbarItems", level=Qgis.Warning) if self.gpsConnection and not self.closeTOMs: self.gps_thread.endGPS() self.disableToolbarItems() # TODO: Need to delete any tools ... for layer, mapTool in self.createMapToolDict.items (): try: status = layer.rollBack() except Exception as e: None """reply = QMessageBox.information(None, "Information", "Problem rolling back changes" + str(self.currLayer.commitErrors()), QMessageBox.Ok)""" del mapTool self.createMapToolDict = {} try: self.iface.currentLayerChanged.disconnect(self.changeCurrLayer2) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for currentLayerChanged {}".format(e), level=Qgis.Warning) try: self.canvas.mapToolSet.disconnect(self.changeMapTool2) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for mapToolSet {}".format( e), level=Qgis.Warning) try: self.canvas.extentsChanged.disconnect(self.changeExtents) except Exception as e: TOMsMessageLog.logMessage( "In disableFeaturesWithGPSToolbarItems. Issue with disconnects for extentsChanged {}".format( e), level=Qgis.Warning) self.tableNames.removePathFromLayerForms() def setCloseCaptureGPSFeaturesFlag(self): self.closeCaptureGPSFeatures = True self.gpsAvailable = True def onGroupTriggered(self, action): # hold the current action self.currGnssAction = action TOMsMessageLog.logMessage("In onGroupTriggered: curr action is {}".format(action.text()), level=Qgis.Info) """ Using signals for ChangeTool and ChangeLayer to manage the tools - with the following functions """ def isGnssTool(self, mapTool): if (isinstance(mapTool, CreateRestrictionTool) or isinstance(mapTool, GeometryInfoMapTool) or isinstance(mapTool, RemoveRestrictionTool)): return True return False def changeMapTool2(self): TOMsMessageLog.logMessage( "In changeMapTool2 ...", level=Qgis.Info) currMapTool = self.iface.mapCanvas().mapTool() if not self.isGnssTool(currMapTool): TOMsMessageLog.logMessage( "In changeMapTool2. Unchecking action ...", level=Qgis.Info) if self.currGnssAction: self.currGnssAction.setChecked(False) else: TOMsMessageLog.logMessage( "In changeMapTool2. No action for gnssTools.", level=Qgis.Info) TOMsMessageLog.logMessage( "In changeMapTool2. finished.", level=Qgis.Info) #print('tool unset') def changeCurrLayer2(self): TOMsMessageLog.logMessage("In changeLayer2 ... ", level=Qgis.Info) try: currMapTool = self.iface.mapCanvas().mapTool() self.currGnssAction.setChecked(False) except Exception as e: None """if self.isGnssTool(currMapTool): TOMsMessageLog.logMessage("In changeLayer2. Action triggered ... ", level=Qgis.Info) self.currGnssAction.trigger() # assumption is that there is an action associated with the tool else: TOMsMessageLog.logMessage( "In changeLayer2. No action for currentMapTool.", level=Qgis.Info)""" TOMsMessageLog.logMessage( "In changeLayer2. finished.", level=Qgis.Info) print('layer changed') def doCreateRestriction(self): TOMsMessageLog.logMessage("In doCreateRestriction", level=Qgis.Info) self.currLayer = self.iface.activeLayer() if not self.currLayer: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return # TODO: Check that this is a restriction layer if self.actionCreateRestriction.isChecked(): TOMsMessageLog.logMessage("In doCreateRestriction - tool activated", level=Qgis.Info) TOMsMessageLog.logMessage( "In doCreateRestriction: current map tool {}".format(type(self.iface.mapCanvas().mapTool()).__name__), level=Qgis.Info) self.createRestrictionMapTool = self.createMapToolDict.get(self.currLayer) if not self.createRestrictionMapTool: TOMsMessageLog.logMessage("In doCreateRestriction. creating new map tool", level=Qgis.Info) self.createRestrictionMapTool = CreateRestrictionTool(self.iface, self.currLayer) self.createMapToolDict[self.currLayer] = self.createRestrictionMapTool TOMsMessageLog.logMessage("In doCreateRestriction. Here 1", level=Qgis.Info) self.iface.mapCanvas().setMapTool(self.createRestrictionMapTool) TOMsMessageLog.logMessage("In doCreateRestriction. Here 2", level=Qgis.Info) if not self.createRestrictionMapTool.isCapturing(): if self.currLayer.isEditable() == True: if self.currLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(self.currLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In doCreateRestriction: changes committed", level=Qgis.Info) if self.currLayer.readOnly() == True: TOMsMessageLog.logMessage("In doCreateRestriction - Not able to start transaction ...", level=Qgis.Info) else: if self.currLayer.startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return TOMsMessageLog.logMessage("In doCreateRestriction. Here 3", level=Qgis.Info) else: TOMsMessageLog.logMessage("In doCreateRestriction - tool deactivated", level=Qgis.Info) if self.createRestrictionMapTool: self.iface.mapCanvas().unsetMapTool(self.createRestrictionMapTool) self.currMapTool = None self.currentlySelectedLayer = None self.actionCreateRestriction.setChecked(False) # TODO: stop editting on layers?? TOMsMessageLog.logMessage("In doCreateRestriction. Here 4", level=Qgis.Info) # -- end of tools for signals def changeExtents(self): TOMsMessageLog.logMessage("In changeExtents ... ", level=Qgis.Info) def doAddGPSLocation(self): # need to have a addPointFromGPS function within each tool TOMsMessageLog.logMessage("In doAddGPSLocation", level=Qgis.Info) if self.gpsConnection: if self.curr_gps_location: try: status = self.createRestrictionMapTool.addPointFromGPS(self.curr_gps_location, self.curr_gps_info) except Exception as e: TOMsMessageLog.logMessage("In doAddGPSLocation: Problem adding gnss location: {}".format(e), level=Qgis.Warning) reply = QMessageBox.information(self.iface.mainWindow(), "Error", "Problem adding gnss location ... ", QMessageBox.Ok) else: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "No position found ...", QMessageBox.Ok) else: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "You need to activate the tool first ...", QMessageBox.Ok) def doRestrictionDetails(self): """ Select point and then display details. Assume that there is only one of these map tools in existence at any one time ?? """ TOMsMessageLog.logMessage("In doRestrictionDetails", level=Qgis.Info) # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools if not self.iface.activeLayer(): reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.actionRestrictionDetails.isChecked(): TOMsMessageLog.logMessage("In doRestrictionDetails - tool activated", level=Qgis.Warning) self.showRestrictionMapTool = GeometryInfoMapTool(self.iface) self.iface.mapCanvas().setMapTool(self.showRestrictionMapTool) self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails) else: TOMsMessageLog.logMessage("In doRestrictionDetails - tool deactivated", level=Qgis.Warning) if self.showRestrictionMapTool: self.iface.mapCanvas().unsetMapTool(self.showRestrictionMapTool) self.actionRestrictionDetails.setChecked(False) #@pyqtSlot(str) def showRestrictionDetails(self, closestLayer, closestFeature): TOMsMessageLog.logMessage( "In showRestrictionDetails ... Layer: " + str(closestLayer.name()), level=Qgis.Info) self.showRestrictionMapTool.notifyFeatureFound.disconnect(self.showRestrictionDetails) # TODO: could improve ... basically check to see if transaction in progress ... if closestLayer.isEditable() == True: reply = QMessageBox.question(None, "Information", "There is a transaction in progress on this layer. This action will rollback back any changes. Do you want to continue?", QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.No: return if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In showRestrictionDetails: changes committed", level=Qgis.Info) """if self.iface.activeLayer().readOnly() == True: TOMsMessageLog.logMessage("In showSignDetails - Not able to start transaction ...", level=Qgis.Info) else: if self.iface.activeLayer().startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return""" self.dialog = self.iface.getFeatureForm(closestLayer, closestFeature) #self.TOMsUtils.setupRestrictionDialog(self.dialog, closestLayer, closestFeature) self.setupFieldRestrictionDialog(self.dialog, closestLayer, closestFeature) self.dialog.show() """ Decided that it is best to use the QGIS select/delete tools to manage removals. So these functions are not used """ def doRemoveRestriction(self): TOMsMessageLog.logMessage("In doRemoveRestriction", level=Qgis.Info) self.currLayer = self.iface.activeLayer() if not self.currLayer: reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.currLayer.readOnly() == True: """reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok)""" TOMsMessageLog.logMessage("In doRemoveRestriction - Not able to start transaction ...", level=Qgis.Info) self.actionRemoveRestriction.setChecked(False) return if self.actionRemoveRestriction.isChecked(): TOMsMessageLog.logMessage("In doRemoveRestriction - tool activated", level=Qgis.Warning) """self.mapTool = self.deleteMapToolDict.get(self.currLayer) if not self.mapTool: self.mapTool = RemoveRestrictionTool(self.iface) self.deleteMapToolDict[self.currLayer] = self.mapTool""" self.mapTool = RemoveRestrictionTool(self.iface) #self.removeRestrictionMapTool.setAction(self.actionRemoveRestriction) self.iface.mapCanvas().setMapTool(self.removeRestrictionMapTool) #self.gpsMapTool = True #self.removeRestrictionMapTool.deactivated.connect(functools.partial(self.deactivateAction, self.actionRemoveRestriction)) #self.iface.currentLayerChanged.connect(self.changeCurrLayer) #self.canvas.mapToolSet.connect(self.changeMapTool) self.removeRestrictionMapTool.notifyFeatureFound.connect(self.removeRestriction) else: TOMsMessageLog.logMessage("In doRemoveRestriction - tool deactivated", level=Qgis.Warning) self.removeRestrictionMapTool.notifyFeatureFound.disconnect(self.removeRestriction) #self.canvas.mapToolSet.disconnect(self.changeMapTool) #self.iface.currentLayerChanged.disconnect(self.changeCurrLayer) self.iface.mapCanvas().unsetMapTool(self.removeRestrictionMapTool) #self.removeRestrictionMapTool.deactivate() #self.mapTool = None self.actionRemoveRestriction.setChecked(False) #@pyqtSlot(str) def removeRestriction(self, closestLayer, closestFeature): TOMsMessageLog.logMessage( "In removeRestriction ... Layer: " + str(closestLayer.name()), level=Qgis.Info) if closestLayer.isEditable() == True: if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info) if self.currLayer.startEditing() == False: reply = QMessageBox.information(None, "Information", "Could not start transaction on " + self.currLayer.name(), QMessageBox.Ok) return # TODO: Sort out this for UPDATE # self.setDefaultRestrictionDetails(closestFeature, closestLayer) closestLayer.deleteFeature(closestFeature.id()) if closestLayer.commitChanges() == False: reply = QMessageBox.information(None, "Information", "Problem committing changes" + str(closestLayer.commitErrors()), QMessageBox.Ok) else: TOMsMessageLog.logMessage("In removeRestriction: changes committed", level=Qgis.Info) """ This is a tool for adding a point feature. currently only used for signs, but could be used for any point """ def doCreateSign(self): TOMsMessageLog.logMessage("In doCreateSign", level=Qgis.Info) if self.actionCreateSign.isChecked(): self.currMapTool = self.canvas.mapTool() self.currentlySelectedLayer = self.iface.activeLayer() self.signsLayer = self.tableNames.setLayer("Signs") self.iface.setActiveLayer(self.signsLayer) self.createPointMapTool = CreatePointTool(self.iface, self.signsLayer) TOMsMessageLog.logMessage("In doCreateSign - tool activated", level=Qgis.Info) self.signsLayer.editingStopped.connect(self.reinstateMapTool) self.actionCreateSign.setChecked(False) self.iface.mapCanvas().setMapTool(self.createPointMapTool) """ add the point from the gnss """ try: status = self.canvas.mapTool().addPointFromGPS(self.curr_gps_location, self.curr_gps_info) except Exception as e: TOMsMessageLog.logMessage("In doCreateSign: Problem adding gnss location: {}".format(e), level=Qgis.Warning) reply = QMessageBox.information(self.iface.mainWindow(), "Error", "Problem adding gnss location ... ", QMessageBox.Ok) """ Not currently used, but want to develop ... """ def doCreateMTR(self): TOMsMessageLog.logMessage("In doCreateMTR", level=Qgis.Info) if self.actionCreateMTR.isChecked(): TOMsMessageLog.logMessage("In doCreateMTR - tool activated", level=Qgis.Info) # Open MTR form ... try: self.thisMtrForm except AttributeError: self.thisMtrForm = mtrForm(self.iface) #res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog) #self.mtrTypeCB = self.dialog.findChild(QComboBox, "cmb_MTR_list") #self.mtrTypeCB.activated[str].connect(self.onLocalChanged) #self.currDialog.findChild(QComboBox, "cmb_MTR_list").activated[str].connect(self.onChanged) """ Need to setup dialog: a. create drop down b. link structure of form to different options from drop down, e.g., Access Restriction needs ?? attributes and one point, Turn Restriction needs ?? attributes and two points c. link getPoint actions to buttons """ status = self.thisMtrForm.show() # Run the dialog event loop result = self.thisMtrForm.exec_() # else: TOMsMessageLog.logMessage("In doCreateMTR - tool deactivated", level=Qgis.Info) #self.iface.mapCanvas().unsetMapTool(self.mapTool) #self.mapTool = None self.actionCreateMTR.setChecked(False) self.gpsMapTool = False def onLocalChanged(self, text): TOMsMessageLog.logMessage( "In generateFirstStageForm::selectionchange. " + text, level=Qgis.Info) res = mtrFormFactory.prepareForm(self.iface, self.dbConn, self.dialog, text) """ Used with the createSign tool to reinstate the last used maptool, i.e., to allow the interupt of feature creation """ def reinstateMapTool(self): TOMsMessageLog.logMessage("In reinstateMapTool ... ", level=Qgis.Info) self.iface.activeLayer().editingStopped.disconnect(self.reinstateMapTool) if self.currMapTool: TOMsMessageLog.logMessage( "In reinstateMapTool. layer to be reinstated {} using tool {}".format(self.currentlySelectedLayer.name(), self.currMapTool.toolName()), level=Qgis.Warning) # now reinstate if self.currentlySelectedLayer: self.iface.setActiveLayer(self.currentlySelectedLayer) self.iface.mapCanvas().setMapTool(self.currMapTool) def doMoveFeatureToDifferentLayer(self): """ Select point and then display details. Assume that there is only one of these map tools in existence at any one time ?? """ TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer", level=Qgis.Info) # TODO: Check whether or not there is a create maptool available. If so, stop this and finish using that/those tools if not self.iface.activeLayer(): reply = QMessageBox.information(self.iface.mainWindow(), "Information", "Please choose a layer ...", QMessageBox.Ok) return if self.actionMoveFeatureToDifferentLayer.isChecked(): TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool activated", level=Qgis.Warning) self.moveFeatureToDifferentLayerMapTool = ChangeLayerMapTool(self.iface, self.localTransaction) self.iface.mapCanvas().setMapTool(self.moveFeatureToDifferentLayerMapTool) #self.showRestrictionMapTool.notifyFeatureFound.connect(self.showRestrictionDetails) else: TOMsMessageLog.logMessage("In doMoveFeatureToDifferentLayer - tool deactivated", level=Qgis.Warning) if self.moveFeatureToDifferentLayerMapTool: self.iface.mapCanvas().unsetMapTool(self.moveFeatureToDifferentLayerMapTool) self.actionMoveFeatureToDifferentLayer.setChecked(False) #@pyqtSlot(QgsGpsConnection) def gpsStarted(self, connection): TOMsMessageLog.logMessage("In enableTools - GPS connection found ", level=Qgis.Info) self.gpsConnection = connection # marker self.marker = QgsVertexMarker(self.canvas) self.marker.setColor(QColor(255, 0, 0)) # (R,G,B) self.marker.setIconSize(10) self.marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.marker.setPenWidth(3) self.enableGnssToolbarItem() reply = QMessageBox.information(None, "Information", "Connection found", QMessageBox.Ok) #@pyqtSlot() def gpsStopped(self): TOMsMessageLog.logMessage("In enableTools - GPS connection stopped ", level=Qgis.Warning) self.gps_thread.deleteLater() self.thread.quit() self.thread.wait() self.thread.deleteLater() if self.gpsConnection: if self.canvas is not None: self.marker.hide() self.canvas.scene().removeItem(self.marker) self.gpsConnection = None self.disableGnssToolbarItem() #@pyqtSlot() def gpsPositionProvided(self, mapPointXY, gpsInfo): """reply = QMessageBox.information(None, "Information", "Position provided", QMessageBox.Ok)""" TOMsMessageLog.logMessage("In enableTools - ******** initial GPS location provided " + mapPointXY.asWkt(), level=Qgis.Info) self.curr_gps_location = mapPointXY self.curr_gps_info = gpsInfo wgs84_pointXY = QgsPointXY(gpsInfo.longitude, gpsInfo.latitude) wgs84_point = QgsPoint(wgs84_pointXY) wgs84_point.transform(self.transformation) x = wgs84_point.x() y = wgs84_point.y() new_mapPointXY = QgsPointXY(x, y) TOMsMessageLog.logMessage("In enableTools - ******** transformed GPS location provided " + str(gpsInfo.longitude) + ":" + str(gpsInfo.latitude) + "; " + new_mapPointXY.asWkt(), level=Qgis.Info) if gpsInfo.pdop >= 1: # gps ok self.marker.setColor(QColor(0, 200, 0)) else: self.marker.setColor(QColor(255, 0, 0)) self.marker.setCenter(mapPointXY) self.marker.show() #self.canvas.setCenter(mapPointXY) """TOMsMessageLog.logMessage("In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)), level=Qgis.Info)""" if self.lastCentre.distance(mapPointXY) > self.roamDistance: self.lastCentre = mapPointXY self.canvas.setCenter(mapPointXY) TOMsMessageLog.logMessage( "In enableTools: distance from last fix: {}".format(self.lastCentre.distance(mapPointXY)), level=Qgis.Warning) self.canvas.refresh() # TODO: populate message bar with details about satellites, etc #@pyqtSlot(Exception, str) def gpsErrorEncountered(self, e): TOMsMessageLog.logMessage("In enableTools - GPS connection has error {}".format(e), level=Qgis.Warning) """self.actionCreateRestriction.setEnabled(False) self.actionAddGPSLocation.setEnabled(False)""" self.disableGnssToolbarItem()
class QGISRedSelectPointTool(QgsMapTool): def __init__(self, button, parent, method, type=1): QgsMapTool.__init__(self, parent.iface.mapCanvas()) self.canvas = parent.iface.mapCanvas() self.iface = parent.iface self.parent = parent self.method = method self.setAction(button) self.type = type # type 1: points; 2: lines; 3: 2-points; 4: 2-line; 5: point-line self.startMarker = QgsVertexMarker(self.iface.mapCanvas()) self.startMarker.setColor(QColor(255, 87, 51)) if self.type == 3 or self.type == 4 or self.type == 5: self.startMarker.setColor(QColor(139, 0, 0)) self.startMarker.setIconSize(15) self.startMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X if self.type == 2 or self.type == 4: self.startMarker.setIconType( QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.startMarker.setPenWidth(3) self.startMarker.hide() self.endMarker = QgsVertexMarker(self.iface.mapCanvas()) self.endMarker.setColor(QColor(0, 128, 0)) self.endMarker.setIconSize(15) self.endMarker.setIconType( QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X if self.type == 4 or self.type == 5: self.endMarker.setIconType( QgsVertexMarker.ICON_X) # or ICON_CROSS, ICON_X self.endMarker.setPenWidth(3) self.endMarker.hide() self.firstPoint = None self.snapper = None self.resetProperties() def activate(self): QgsMapTool.activate(self) type = 1 if self.type == 2 or self.type == 4: type = 2 self.configSnapper(type) def deactivate(self): self.resetProperties() QgsMapTool.deactivate(self) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True """Methods""" def configSnapper(self, type): # Snapping self.snapper = QgsMapCanvasSnappingUtils(self.iface.mapCanvas()) self.snapper.setMapSettings(self.iface.mapCanvas().mapSettings()) config = QgsSnappingConfig(QgsProject.instance()) config.setType(type) # 1: Vertex; 2:Segment config.setMode(2) # All layers config.setTolerance(10) config.setUnits(1) # Pixels config.setEnabled(True) self.snapper.setConfig(config) def resetProperties(self): self.firstPoint = None self.startMarker.hide() self.endMarker.hide() self.objectSnapped = None """Events""" def canvasReleaseEvent(self, event): if event.button() == Qt.LeftButton: if self.objectSnapped is None: self.iface.messageBar().pushMessage( "Warning", "A not valid point was selected", level=1, duration=5) return if self.type == 3 or self.type == 4 or self.type == 5: if self.firstPoint is None: self.firstPoint = self.objectSnapped.point() if self.type == 5: self.configSnapper(2) else: point1 = self.firstPoint point2 = self.objectSnapped.point() # Call to parent method self.method(point1, point2) self.resetProperties() if self.type == 5: self.configSnapper(1) else: point = self.objectSnapped.point() # Call to parent method self.method(point) self.resetProperties() if event.button() == Qt.RightButton: if self.type == 3 or self.type == 5: if self.objectSnapped is None: self.iface.messageBar().pushMessage( "Warning", "A not valid point was selected", level=1, duration=5) return else: point = self.objectSnapped.point() # Call to parent method # Tconnection & Split/merge Juncitons self.method(point, None) self.resetProperties() else: self.canvas.unsetMapTool(self) self.deactivate() return def canvasMoveEvent(self, event): match = self.snapper.snapToMap(self.toMapCoordinates(event.pos())) if match.isValid(): self.objectSnapped = match if self.firstPoint is None: self.startMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.startMarker.show() else: self.endMarker.setCenter( QgsPointXY(match.point().x(), match.point().y())) self.endMarker.show() else: self.startMarker.hide() self.endMarker.hide() self.objectSnapped = None
class GTOPointWidget(QWidget): isActive = pyqtSignal(bool) def __init__(self, gtoObj, parent=None): super(GTOPointWidget, self).__init__(parent) self.gtomain = gtoObj.gtomain self.info = self.gtomain.info self.debug = self.gtomain.debug try: # references self.helper = self.gtomain.helper self.iface = self.gtomain.iface self.prj = QgsProject.instance() self.canvas = self.iface.mapCanvas() # references self.x = 0 self.y = 0 self.xt = 0 self.yt = 0 self.snaped = False self.crs_transform = None self.crs_layer = None self.parent_widget = None # e.g toolbar self.userEditX = False self.userEditY = False # config self.tools = [] self.coordinatereferences = None self.scale = 0 self.center = True self.enable_save = False self.precision = -1 self.cboCoordSystems = None self.is_widgetaction = False self.show_tool_button = False self.addpoint_attributes = {} self.tools_after_addpoint = [] # widgets: uic.loadUi(os.path.join(os.path.dirname(__file__), 'gto_point.ui'), self) # point tool self.btnPointTool = self.btnPoint # x self.coordX = self.coordX # self.validX = QDoubleValidator(sys.float_info.min, sys.float_info.max, 16, self.coordX) # no negative numbers possible? # self.validX = QDoubleValidator(-999999999, 999999999, 16, self.coordX) # working but no range limit self.validX = QDoubleValidator(self.coordX) # so we use the standard: self.validX.setNotation(QDoubleValidator.StandardNotation) # By default, this property is set to ScientificNotation: i.e. 1.5E-2 is possible self.coordX.setValidator(self.validX) self.btnCopyXt = self.btnCopyXt self.lblX = self.lblX # y self.coordY = self.coordY self.validY = QDoubleValidator(self.coordY) self.validY.setNotation(QDoubleValidator.StandardNotation) self.coordY.setValidator(self.validY) self.btnCopyYt = self.btnCopyYt self.lblY = self.lblY # show self.btnShow = self.btnShow self.btnShow.setIcon(self.helper.getIcon('mActionZoomPoint.png')) # add point self.btnAddPoint = self.btnAddPoint self.btnAddPoint.setIcon(self.helper.getIcon('mActionAddPoint.png')) self.btnAddPoint.setToolTip("Punkt erstellen") # marker self.marker = QgsVertexMarker(self.canvas) self.marker.setColor(Qt.yellow) self.marker.setIconType(QgsVertexMarker.ICON_CROSS) self.marker.setIconSize(10) self.marker.setPenWidth(3) # See the enum IconType from http://www.qgis.org/api/classQgsVertexMarker.html # maptool self.mapTool = GTOPointTool(self.iface, self.canvas) self.mapTool.isActive.connect(self.setToolStatus) self.mapTool.canvasReleased.connect(self.setCoords) # signals # QToolButton.toggled() self.btnPoint.clicked.connect(self.setMapTool) # self.coordX.textChanged.connect(self.set_user_editX) # self.coordY.textChanged.connect(self.set_user_editY) self.coordX.textEdited.connect(self.set_user_editX) self.coordY.textEdited.connect(self.set_user_editY) # self.coordX.editingFinished.connect(self.check_coords) # self.coordY.editingFinished.connect(self.check_coords) self.btnShow.clicked.connect(self.showCoordinate) self.btnCopyXt.clicked.connect(self.copyXt) self.btnCopyYt.clicked.connect(self.copyYt) self.btnAddPoint.clicked.connect(self.add_point) self.prj.crsChanged.connect(self.prj_crs_changed) self.iface.mapCanvas().currentLayerChanged.connect(self.layer_changed) except Exception as e: self.info.err(e) def set_user_editX(self, *args): try: if self.debug: self.info.log("set_user_editX") self.userEditX = True self.marker.hide() self.marker.setColor(Qt.blue) self.snaped = False except Exception as e: self.info.err(e) def set_user_editY(self, *args): try: if self.debug: self.info.log("set_user_editY") self.userEditY = True self.marker.hide() self.marker.setColor(Qt.blue) self.snaped = False except Exception as e: self.info.err(e) def reset_user_edit(self): if self.debug: self.info.log("reset_user_edit") self.userEditX = False self.userEditY = False def check_coords(self): try: self.marker.hide() if self.debug: self.info.log("useredit: X:", self.userEditX, "userEditY:", self.userEditY) if self.coordX.text() == '': self.coordX.setText('0') self.x = 0 if self.coordY.text() == '': self.coordY.setText('0') self.y = 0 if self.userEditX or self.userEditY: self.snaped = False self.userEditX = False self.userEditY = False self.xt = float(self.coordX.text().replace(",", ".")) self.yt = float(self.coordY.text().replace(",", ".")) tr = QgsCoordinateTransform(self.crs_transform, self.prj.crs(), self.prj) trPoint = tr.transform(QgsPointXY(self.xt, self.yt)) self.x = trPoint.x() self.y = trPoint.y() if self.debug: self.info.log("check_coords:", self.x, "/", self.y, "/snaped:", self.snaped) except Exception as e: self.info.err(e) def setMapTool(self): try: self.canvas.setMapTool(self.mapTool) except Exception as e: self.info.err(e) def set_parent_widget(self, widget): try: self.parent_widget = widget if self.parent_widget.action.isChecked(): self.setMapTool() except Exception as e: self.info.err(e) def setToolStatus(self, isActive): try: self.btnPoint.setChecked(isActive) self.marker.hide() self.isActive.emit(isActive) if self.parent_widget is not None: self.parent_widget.set_status(isActive) except Exception as e: self.info.err(e) def setConfig(self, config): try: self.tools = config.get("tools", []) self.coordinatereferences = config.get("coordinatereferences", None) self.scale = config.get("scale", 0) self.center = config.get("center", True) self.enable_save = config.get('enable_save', False) self.precision = config.get('precision', -1) self.is_widgetaction = config.get('is_widgetaction', False) self.show_tool_button = config.get('show_tool_button', not self.is_widgetaction) self.addpoint_attributes = config.get("addpoint_attributes", {}) self.tools_after_addpoint = config.get("tools_after_addpoint", []) if self.precision < 0: self.precision, type_conversion_ok = self.prj.readNumEntry("PositionPrecision", "DecimalPlaces", 3) # labels: self.lblX.setText(config.get('label_x', 'X:')) self.lblY.setText(config.get('label_y', 'Y:')) # text text = '' if self.scale > 0 and self.center: text = "Auf Koordinate zentrieren, Maßstab: " + str(self.scale) elif self.center: text = "Auf Koordinate zentrieren" elif self.scale > 0: text = "Maßstab: " + str(self.scale) elif len(self.tools) > 0: text = self.tools[0] act = self.gtomain.findAction(self.tools[0]) if act is not None: text = act.toolTip() if act.icon() is not None: self.btnShow.setIcon(act.icon()) if self.debug: self.info.log(text) self.btnShow.setToolTip(text) if self.btnShow.toolTip() == '': self.btnShow.setHidden(True) # add point self.btnAddPoint.setHidden(not self.enable_save) # point tool self.btnPointTool.setHidden(not self.show_tool_button) except Exception as e: self.info.err(e) def added(self): # widget was added to parent try: self.crs_transform = self.prj.crs() self.crs_layer = self.iface.activeLayer().crs() # set crs widget if self.coordinatereferences is None: # qgis transform self.cboCoordSys.setHidden(True) self.cboCoordSystems = self.mQgsProjectionSelectionWidget self.cboCoordSystems.setMinimumWidth(460) self.cboCoordSystems.setOptionVisible(QgsProjectionSelectionWidget.ProjectCrs, True) self.cboCoordSystems.setCrs(self.prj.crs()) self.setCrs(self.cboCoordSystems.crs()) self.cboCoordSystems.crsChanged.connect(self.setCrs) else: # custom transform self.mQgsProjectionSelectionWidget.setHidden(True) self.cboCoordSystems = self.cboCoordSys self.cboCoordSystems.setMinimumWidth(400) self.cboCoordSystems.currentIndexChanged.connect( lambda: self.setCrs(self.cboCoordSystems.currentData())) self.cboCoordSystems.addItem( "Projekt CRS: " + self.crs_transform.authid() + " - " + self.crs_transform.description(), self.crs_transform) for crsID in self.coordinatereferences: try: crs = QgsCoordinateReferenceSystem(crsID) self.cboCoordSystems.addItem(crs.authid() + " - " + crs.description(), crs) except Exception as e: self.info.err(e) self.cboCoordSystems.setCurrentIndex(0) # here we know which type is cboCoordSystems! self.setIconSizes() except Exception as e: self.info.err(e) def setIconSizes(self): try: if self.parentWidget() is not None: btns = self.findChildren(QToolButton) for btn in btns: try: btn.setIconSize(self.iface.iconSize(False)) except: pass # help for the QGIS widget :S self.cboCoordSystems.setMaximumHeight(self.cboCoordSys.height()) btns = self.cboCoordSystems.findChildren(QToolButton) for btn in btns: btn.setIconSize(self.iface.iconSize(False)) except Exception as e: self.info.err(e) def layer_changed(self, layer): try: if layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry: self.btnAddPoint.setEnabled(True) else: self.btnAddPoint.setEnabled(False) except Exception as e: self.info.err(e) def prj_crs_changed(self): try: self.reset_user_edit() if self.coordinatereferences is not None: # my combo self.crs_transform = self.prj.crs() self.cboCoordSystems.setItemText(0, "Projekt CRS: " + self.crs_transform.authid() + " - " + self.crs_transform.description()) self.cboCoordSystems.setItemData(0, self.crs_transform) self.x = 0 self.y = 0 self.xt = 0 self.yt = 0 self.coordX.setText("---") self.coordY.setText("---") except Exception as e: self.info.err(e) def add_point(self): try: self.check_coords() layer = self.iface.activeLayer() if layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry: self.prj.layerTreeRoot().findLayer(layer.id()).setItemVisibilityCheckedParentRecursive(True) if self.x != 0 and self.y != 0: feat = QgsVectorLayerUtils.createFeature(layer) tr = QgsCoordinateTransform(self.prj.crs(), self.crs_layer, self.prj) trPoint = tr.transform(QgsPointXY(self.x, self.y)) feat.setGeometry(QgsGeometry.fromPointXY(trPoint)) # direct save # (res, features) = layer.dataProvider().addFeatures([feat]) # if self.debug: self.info.log("new point:", res, features[0]) # set attributes dic_info = {"x": self.x, "y": self.y, "snaped": self.snaped} # self.info.err(None,"mapping:",dic_info) # self.info.err(None, "addpoint_attributes:", self.addpoint_attributes) for k, v in self.addpoint_attributes.items(): # self.info.err(None,"attribute:",k,"value:",dic_info[v]) feat[k] = layer.fields().field(k).convertCompatible(dic_info[v]) features = [feat] layer.featureAdded.connect(self.select_new_feature) self.save_features(layer, features) layer.featureAdded.disconnect(self.select_new_feature) self.marker.hide() self.helper.refreshLayer(layer) self.gtomain.runcmd(self.tools_after_addpoint) else: self.info.gtoWarning('Ungültige Koordinaten! x:', self.x, "y:", self.y) else: self.info.gtoWarning('Kein Punktlayer ausgewählt!') except Exception as e: self.info.err(e) def select_new_feature(self, featId): try: if self.debug: self.info.log("new featue:", self.iface.activeLayer().name(), "/ fid:", featId) self.iface.activeLayer().selectByIds([featId]) self.mapTool.reset_marker() self.marker.hide() self.helper.refreshLayer(self.iface.activeLayer()) except Exception as e: self.info.err(e) def save_features(self, layer, features): if not layer.isEditable(): layer.startEditing() layer.beginEditCommand("layer {0} edit".format(layer.name())) try: layer.addFeatures(features) layer.endEditCommand() except Exception as e: layer.destroyEditCommand() raise e def copyXt(self): self.check_coords() dsp = QDoubleSpinBox() dsp.setDecimals(16) self.helper.copyToClipboard(dsp.textFromValue(self.xt)) def copyYt(self): self.check_coords() dsp = QDoubleSpinBox() dsp.setDecimals(16) self.helper.copyToClipboard(dsp.textFromValue(self.yt)) def reset(self): if self.debug: self.info.log("widget reset") self.marker.hide() def setCoords(self, point, snaped): try: self.reset_user_edit() self.snaped = snaped self.x = point.x() self.y = point.y() if self.debug: self.info.log("setCoords", self.x, "/", self.y) self.setCrs(self.crs_transform) # marker self.marker.setCenter(QgsPointXY(self.x, self.y)) if snaped: self.marker.setColor(Qt.red) else: self.marker.setColor(Qt.blue) self.marker.show() except Exception as e: self.info.err(e) def showCoordinate(self): try: self.check_coords() self.marker.hide() if self.x != 0 and self.y != 0: pt_center = QgsPointXY(self.x, self.y) self.marker.setCenter(pt_center) self.marker.show() # center map if self.center: self.canvas.setCenter(pt_center) # scale map if self.scale is not None and self.scale > 0: self.canvas.zoomScale(self.scale) self.canvas.refresh() # run tools self.gtomain.runcmd(self.tools) else: self.info.gtoWarning('Ungültige Koordinate! x:', self.x, "y:", self.y) except Exception as e: self.info.err(e) def setCrs(self, crs): try: if self.debug: self.info.log("setCrs") self.crs_transform = crs tr = QgsCoordinateTransform(self.prj.crs(), self.crs_transform, self.prj) trPoint = tr.transform(QgsPointXY(self.x, self.y)) self.xt = trPoint.x() self.yt = trPoint.y() d = round(trPoint.x(), self.precision) display = str(d).replace(".", QLocale().decimalPoint()) self.coordX.setText(display) d = round(trPoint.y(), self.precision) display = str(d).replace(".", QLocale().decimalPoint()) self.coordY.setText(display) except Exception as e: self.info.err(e)
class ManNodeDialog(ParentDialog): def __init__(self, dialog, layer, feature): """ Constructor class """ self.geom_type = "node" self.field_id = "node_id" self.id = utils_giswater.getWidgetText(self.field_id, False) super(ManNodeDialog, self).__init__(dialog, layer, feature) self.init_config_form() self.controller.manage_translation('ud_man_node', dialog) if dialog.parent(): dialog.parent().setFixedSize(625, 660) def init_config_form(self): """ Custom form initial configuration """ # Define class variables self.filter = self.field_id + " = '" + str(self.id) + "'" # Get widget controls self.tab_main = self.dialog.findChild(QTabWidget, "tab_main") self.tbl_element = self.dialog.findChild(QTableView, "tbl_element") self.tbl_document = self.dialog.findChild(QTableView, "tbl_document") self.tbl_event_element = self.dialog.findChild(QTableView, "tbl_event_element") self.tbl_event = self.dialog.findChild(QTableView, "tbl_event_node") self.tbl_scada = self.dialog.findChild(QTableView, "tbl_scada") self.tbl_scada_value = self.dialog.findChild(QTableView, "tbl_scada_value") self.tbl_costs = self.dialog.findChild(QTableView, "tbl_masterplan") self.nodecat_id = self.dialog.findChild(QLineEdit, 'nodecat_id') self.node_type = self.dialog.findChild(QComboBox, 'node_type') state_type = self.dialog.findChild(QComboBox, 'state_type') dma_id = self.dialog.findChild(QComboBox, 'dma_id') # Tables self.tbl_upstream = self.dialog.findChild(QTableView, "tbl_upstream") self.tbl_upstream.setSelectionBehavior(QAbstractItemView.SelectRows) self.tbl_downstream = self.dialog.findChild(QTableView, "tbl_downstream") self.tbl_downstream.setSelectionBehavior(QAbstractItemView.SelectRows) self.dialog.findChild(QPushButton, "btn_catalog").clicked.connect( partial(self.catalog, 'ud', 'node')) self.tbl_upstream.doubleClicked.connect( partial(self.open_up_down_stream, self.tbl_upstream)) self.tbl_downstream.doubleClicked.connect( partial(self.open_up_down_stream, self.tbl_downstream)) feature = self.feature layer = self.iface.activeLayer() # Toolbar actions action = self.dialog.findChild(QAction, "actionEnabled") action.setChecked(layer.isEditable()) self.dialog.findChild(QAction, "actionCopyPaste").setEnabled(layer.isEditable()) self.dialog.findChild(QAction, "actionRotation").setEnabled(layer.isEditable()) self.dialog.findChild(QAction, "actionZoom").triggered.connect( partial(self.action_zoom_in, feature, self.canvas, layer)) self.dialog.findChild(QAction, "actionCentered").triggered.connect( partial(self.action_centered, feature, self.canvas, layer)) self.dialog.findChild(QAction, "actionEnabled").triggered.connect( partial(self.action_enabled, action, layer)) self.dialog.findChild(QAction, "actionZoomOut").triggered.connect( partial(self.action_zoom_out, feature, self.canvas, layer)) self.dialog.findChild(QAction, "actionRotation").triggered.connect( self.action_rotation) self.dialog.findChild(QAction, "actionCopyPaste").triggered.connect( partial(self.action_copy_paste, self.geom_type)) self.dialog.findChild(QAction, "actionLink").triggered.connect( partial(self.check_link, True)) self.dialog.findChild(QAction, "actionHelp").triggered.connect( partial(self.action_help, 'ud', 'node')) # Manage custom fields cat_feature_id = utils_giswater.getWidgetText(self.node_type) tab_custom_fields = 1 self.manage_custom_fields(cat_feature_id, tab_custom_fields) # Manage tab 'Scada' self.manage_tab_scada() # Check if exist URL from field 'link' in main tab self.check_link() # Check topology for new features continue_insert = True node_over_node = True check_topology_node = self.controller.plugin_settings_value( "check_topology_node", "0") check_topology_arc = self.controller.plugin_settings_value( "check_topology_arc", "0") # Check if feature has geometry object geometry = self.feature.geometry() if geometry: self.controller.log_info("check") if self.id.upper() == 'NULL' and check_topology_node == "0": self.get_topology_parameters() (continue_insert, node_over_node) = self.check_topology_node() if continue_insert and not node_over_node: if self.id.upper() == 'NULL' and check_topology_arc == "0": self.check_topology_arc() # Create thread thread1 = Thread(self, self.controller, 3) thread1.start() self.filter = "node_id = '" + str(self.id) + "'" table_name = self.controller.schema_name + ".v_ui_node_x_connection_upstream" self.fill_table(self.tbl_upstream, table_name, self.filter) self.set_configuration(self.tbl_upstream, "v_ui_node_x_connection_upstream") table_name = self.controller.schema_name + ".v_ui_node_x_connection_downstream" self.fill_table(self.tbl_downstream, table_name, self.filter) self.set_configuration(self.tbl_downstream, "v_ui_node_x_connection_downstream") # Manage tab signal self.tab_connections_loaded = False self.tab_element_loaded = False self.tab_document_loaded = False self.tab_om_loaded = False self.tab_scada_loaded = False self.tab_cost_loaded = False self.tab_main.currentChanged.connect(self.tab_activation) # Load default settings widget_id = self.dialog.findChild(QLineEdit, 'node_id') if utils_giswater.getWidgetText(widget_id).lower() == 'null': self.load_default() self.load_type_default("nodecat_id", "nodecat_vdefault") self.load_state_type(state_type, self.geom_type) self.load_dma(dma_id, self.geom_type) def open_up_down_stream(self, qtable): """ Open selected node from @qtable """ selected_list = qtable.selectionModel().selectedRows() if len(selected_list) == 0: message = "Any record selected" self.controller.show_warning(message) return row = selected_list[0].row() feature_id = qtable.model().record(row).value("feature_id") featurecat_id = qtable.model().record(row).value("featurecat_id") # Get sys_feature_cat.id from cat_feature.id sql = ("SELECT sys_feature_cat.id FROM " + self.controller.schema_name + ".cat_feature" " INNER JOIN " + self.controller.schema_name + ".sys_feature_cat ON cat_feature.system_id = sys_feature_cat.id" " WHERE cat_feature.id = '" + featurecat_id + "'") row = self.controller.get_row(sql) if not row: return layer = self.get_layer(row[0]) if layer: field_id = self.controller.get_layer_primary_key(layer) aux = "\"" + field_id + "\" = " aux += "'" + str(feature_id) + "'" expr = QgsExpression(aux) if expr.hasParserError(): message = "Expression Error" self.controller.show_warning( message, parameter=expr.parserErrorString()) return it = layer.getFeatures(QgsFeatureRequest(expr)) features = [i for i in it] if features != []: self.iface.openFeatureForm(layer, features[0]) else: message = "Layer not found" self.controller.show_warning(message, parameter=row[0]) def get_topology_parameters(self): """ Get parameters 'node_proximity' and 'node2arc' from config table """ self.node_proximity = 0.5 self.node2arc = 0.5 sql = "SELECT node_proximity, node2arc FROM " + self.schema_name + ".config" row = self.controller.get_row(sql) if row: self.node_proximity = row['node_proximity'] self.node2arc = row['node2arc'] def check_topology_arc(self): """ Check topology: Inserted node is over an existing arc? """ # Initialize plugin parameters self.controller.plugin_settings_set_value("check_topology_arc", "0") self.controller.plugin_settings_set_value("close_dlg", "0") # Get selected srid and coordinates. Set SQL to check topology srid = self.controller.plugin_settings_value('srid') point = self.feature.geometry().asPoint() node_geom = "ST_SetSRID(ST_Point(" + str(point.x()) + ", " + str( point.y()) + "), " + str(srid) + ")" sql = ("SELECT arc_id, state FROM " + self.schema_name + ".v_edit_arc" " WHERE ST_DWithin(" + node_geom + ", v_edit_arc.the_geom, " + str(self.node2arc) + ")" " ORDER BY ST_Distance(v_edit_arc.the_geom, " + node_geom + ")" " LIMIT 1") row = self.controller.get_row(sql) if row: msg = ( "We have detected you are trying to divide an arc with state " + str(row['state']) + "" "\nRemember that:" "\n\nIn case of arc has state 0, you are allowed to insert a new node, because state 0 has not topology rules, and as a result arc will not be broken." "\nIn case of arc has state 1, only nodes with state=1 can be part of node1 or node2 from arc. If the new node has state 0 or state 2 arc will be broken." "\nIn case of arc has state 2, nodes with state 1 or state 2 are enabled. If the new node has state 0 arc will not be broken" "\n\nWould you like to continue?") answer = self.controller.ask_question(msg, "Divide intersected arc?") if answer: self.controller.plugin_settings_set_value( "check_topology_arc", "1") else: self.controller.plugin_settings_set_value("close_dlg", "1") def check_topology_node(self): """ Check topology: Inserted node is over an existing node? """ node_over_node = False continue_insert = True # Initialize plugin parameters self.controller.plugin_settings_set_value("check_topology_node", "0") self.controller.plugin_settings_set_value("close_dlg", "0") # Get selected srid and coordinates. Set SQL to check topology srid = self.controller.plugin_settings_value('srid') point = self.feature.geometry().asPoint() node_geom = "ST_SetSRID(ST_Point(" + str(point.x()) + ", " + str( point.y()) + "), " + str(srid) + ")" sql = ("SELECT node_id, state FROM " + self.schema_name + ".v_edit_node" " WHERE ST_Intersects(ST_Buffer(" + node_geom + ", " + str(self.node_proximity) + "), the_geom)" " ORDER BY ST_Distance(" + node_geom + ", the_geom)" " LIMIT 1") row = self.controller.get_row(sql) if row: node_over_node = True msg = ( "We have detected you are trying to insert one node over another node with state " + str(row['state']) + "" "\nRemember that:" "\n\nIn case of old or new node has state 0, you are allowed to insert new one, because state 0 has not topology rules." "\nIn the rest of cases, remember that the state topology rules of Giswater only enables one node with the same state at the same position." "\n\nWould you like to continue?") answer = self.controller.ask_question(msg, "Insert node over node?") if answer: self.controller.plugin_settings_set_value( "check_topology_node", "1") else: self.controller.plugin_settings_set_value("close_dlg", "1") continue_insert = False return (continue_insert, node_over_node) def action_rotation(self): # Set map tool emit point and signals self.emit_point = QgsMapToolEmitPoint(self.canvas) self.canvas.setMapTool(self.emit_point) self.snapper = QgsMapCanvasSnapper(self.canvas) self.canvas.xyCoordinates.connect(self.action_rotation_mouse_move) self.emit_point.canvasClicked.connect( self.action_rotation_canvas_clicked) # Store user snapping configuration self.snapper_manager = SnappingConfigManager(self.iface) self.snapper_manager.store_snapping_options() # Clear snapping self.snapper_manager.clear_snapping() # Set snapping layer = self.controller.get_layer_by_tablename("v_edit_arc") self.snapper_manager.snap_to_layer(layer) layer = self.controller.get_layer_by_tablename("v_edit_connec") self.snapper_manager.snap_to_layer(layer) layer = self.controller.get_layer_by_tablename("v_edit_node") self.snapper_manager.snap_to_layer(layer) # Set marker color = QColor(255, 100, 255) self.vertex_marker = QgsVertexMarker(self.canvas) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CROSS) self.vertex_marker.setColor(color) self.vertex_marker.setIconSize(15) self.vertex_marker.setPenWidth(3) def action_rotation_mouse_move(self, point): """ Slot function when mouse is moved in the canvas. Add marker if any feature is snapped """ # Hide marker and get coordinates self.vertex_marker.hide() map_point = self.canvas.getCoordinateTransform().transform(point) x = map_point.x() y = map_point.y() event_point = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers( event_point) # @UnusedVariable if not result: return # Check snapped features for snapped_point in result: point = QgsPoint(snapped_point.snappedVertex) self.vertex_marker.setCenter(point) self.vertex_marker.show() break def action_rotation_canvas_clicked(self, point, btn): if btn == Qt.RightButton: self.disable_rotation() return viewname = self.controller.get_layer_source_table_name(self.layer) sql = ("SELECT ST_X(the_geom), ST_Y(the_geom)" " FROM " + self.schema_name + "." + viewname + "" " WHERE node_id = '" + self.id + "'") row = self.controller.get_row(sql) if row: existing_point_x = row[0] existing_point_y = row[1] sql = ("UPDATE " + self.schema_name + ".node" " SET hemisphere = (SELECT degrees(ST_Azimuth(ST_Point(" + str(existing_point_x) + ", " + str(existing_point_y) + "), " " ST_Point(" + str(point.x()) + ", " + str(point.y()) + "))))" " WHERE node_id = '" + str(self.id) + "'") status = self.controller.execute_sql(sql) if not status: self.disable_rotation() return sql = ("SELECT degrees(ST_Azimuth(ST_Point(" + str(existing_point_x) + ", " + str(existing_point_y) + ")," " ST_Point( " + str(point.x()) + ", " + str(point.y()) + ")))") row = self.controller.get_row(sql) if row: utils_giswater.setWidgetText("hemisphere", str(row[0])) message = "Hemisphere of the node has been updated. Value is" self.controller.show_info(message, parameter=str(row[0])) self.disable_rotation() def disable_rotation(self): """ Disable actionRotation and set action 'Identify' """ action_widget = self.dialog.findChild(QAction, "actionRotation") if action_widget: action_widget.setChecked(False) try: self.snapper_manager.recover_snapping_options() self.vertex_marker.hide() self.set_action_identify() self.canvas.xyCoordinates.disconnect() self.emit_point.canvasClicked.disconnect() except: pass def tab_activation(self): """ Call functions depend on tab selection """ # Get index of selected tab index_tab = self.tab_main.currentIndex() # Tab 'Connections' if index_tab == ( 2 - self.tabs_removed) and not self.tab_connections_loaded: self.fill_tab_connections() self.tab_connections_loaded = True # Tab 'Element' elif index_tab == (3 - self.tabs_removed) and not self.tab_element_loaded: self.fill_tab_element() self.tab_element_loaded = True # Tab 'Document' elif index_tab == (4 - self.tabs_removed) and not self.tab_document_loaded: self.fill_tab_document() self.tab_document_loaded = True # Tab 'O&M' elif index_tab == (5 - self.tabs_removed) and not self.tab_om_loaded: self.fill_tab_om() self.tab_om_loaded = True # Tab 'Scada' elif index_tab == (6 - self.tabs_removed) and not self.tab_scada_loaded: self.fill_tab_scada() self.tab_scada_loaded = True # Tab 'Cost' elif index_tab == (7 - self.tabs_removed) and not self.tab_cost_loaded: self.fill_tab_cost() self.tab_cost_loaded = True def fill_tab_connections(self): """ Fill tab 'Connections' """ self.fill_tables(self.tbl_upstream, "v_ui_node_x_connection_upstream") self.fill_tables(self.tbl_downstream, "v_ui_node_x_connection_downstream") def fill_tab_element(self): """ Fill tab 'Element' """ table_element = "v_ui_element_x_node" self.fill_tbl_element_man(self.tbl_element, table_element, self.filter) self.set_configuration(self.tbl_element, table_element) def fill_tab_document(self): """ Fill tab 'Document' """ table_document = "v_ui_doc_x_node" self.fill_tbl_document_man(self.tbl_document, table_document, self.filter) self.set_configuration(self.tbl_document, table_document) def fill_tab_om(self): """ Fill tab 'O&M' (event) """ table_event_node = "v_ui_om_visit_x_node" self.fill_tbl_event(self.tbl_event, self.schema_name + "." + table_event_node, self.filter) self.tbl_event.doubleClicked.connect(self.open_selected_document_event) self.set_configuration(self.tbl_event, table_event_node) def fill_tab_scada(self): """ Fill tab 'Scada' """ pass def fill_tab_cost(self): """ Fill tab 'Cost' """ table_costs = "v_plan_node" self.fill_table(self.tbl_costs, self.schema_name + "." + table_costs, self.filter) self.set_configuration(self.tbl_costs, table_costs)
class AddPumpTool(QgsMapTool): def __init__(self, data_dock, params): QgsMapTool.__init__(self, data_dock.iface.mapCanvas()) self.iface = data_dock.iface """:type : QgisInterface""" self.data_dock = data_dock """:type : DataDock""" self.params = params self.mouse_pt = None self.mouse_clicked = False self.snapper = None self.snapped_pipe_id = None self.snapped_vertex = None self.snapped_vertex_nr = None self.vertex_marker = QgsVertexMarker(self.canvas()) self.elev = -1 def canvasPressEvent(self, event): if event.button() == Qt.RightButton: self.mouse_clicked = True if event.button() == Qt.LeftButton: self.mouse_clicked = True def canvasMoveEvent(self, event): self.mouse_pt = self.toMapCoordinates(event.pos()) elev = raster_utils.read_layer_val_from_coord(self.params.dem_rlay, self.mouse_pt, 1) self.elev = elev if elev is not None: self.data_dock.lbl_elev_val.setText("{0:.2f}".format(self.elev)) else: self.data_dock.lbl_elev_val.setText('-') if not self.mouse_clicked: # Mouse not clicked: snapping to closest vertex # (retval, result) = self.snapper.snapMapPoint(self.toMapCoordinates(event.pos())) # if len(result) > 0: match = self.snapper.snapToMap(self.mouse_pt) if match.isValid(): # It's a vertex on an existing pipe # self.snapped_pipe_id = result[0].snappedAtGeometry # snapped_vertex = result[0].snappedVertex # self.snapped_vertex_nr = result[0].snappedVertexNr # self.snapped_vertex = QgsPoint(snapped_vertex.x(), snapped_vertex.y()) self.snapped_pipe_id = match.featureId() self.snapped_vertex = match.point() self.snapped_vertex_nr = match.vertexIndex() self.vertex_marker.setCenter(self.snapped_vertex) self.vertex_marker.setColor(QColor(255, 0, 0)) self.vertex_marker.setIconSize(10) self.vertex_marker.setIconType(QgsVertexMarker.ICON_CIRCLE) self.vertex_marker.setPenWidth(3) self.vertex_marker.show() else: # It's a new, isolated vertex self.snapped_pipe_id = None self.vertex_marker.hide() def canvasReleaseEvent(self, event): if not self.mouse_clicked: return if event.button() == Qt.LeftButton: self.mouse_clicked = False # No pipe snapped: notify user if self.snapped_pipe_id is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'You need to snap the cursor to a pipe to add a pump.', Qgis.Info, 5) # A pipe has been snapped else: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: # Check whether the pipe has a start and an end node (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry()) if not start_node or not end_node: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pipe is missing the start or end nodes.', Qgis.Warning, 5) # TODO: softcode return # Find endnode closest to pump position dist_1 = start_node.geometry().distance( QgsGeometry.fromPointXY(self.snapped_vertex)) dist_2 = end_node.geometry().distance( QgsGeometry.fromPointXY(self.snapped_vertex)) # Get the attributes of the closest junction (start_node, end_node) = NetworkUtils.find_start_end_nodes( self.params, features[0].geometry(), False, True, True) if dist_1 < dist_2: closest_junction_ft = start_node else: closest_junction_ft = end_node # Create the pump pump_param = '' pump_head = None pump_power = None pump_speed = 0 pump_speed_pattern = 0 # Head and curve if self.data_dock.cbo_pump_param.itemText( self.data_dock.cbo_pump_param.currentIndex( )) == Pump.parameters_head: pump_param = Pump.parameters_head curve = self.data_dock.cbo_pump_head.itemData( self.data_dock.cbo_pump_head.currentIndex()) if curve is not None: pump_head = curve.id else: pump_head = None # Power and value elif self.data_dock.cbo_pump_param.itemText( self.data_dock.cbo_pump_param.currentIndex( )) == Pump.parameters_power: pump_param = Pump.parameters_power pump_power = float( self.data_dock.txt_pump_power.text()) # Speed pump_speed_s = self.data_dock.txt_pump_speed.text() if pump_speed_s is None or pump_speed_s == '': pump_speed = 0 else: pump_speed = float(pump_speed_s) # Speed pattern pump_speed_pattern = self.data_dock.cbo_pump_speed_pattern.itemText( self.data_dock.cbo_pump_speed_pattern.currentIndex()) # Pump status pump_status = self.data_dock.cbo_pump_status.itemData( self.data_dock.cbo_pump_status.currentIndex()) # Pump description pump_desc = self.data_dock.txt_pump_desc.text() # Pump tag pump_tag = self.data_dock.cbo_pump_tag.currentText() try: LinkHandler.create_new_pumpvalve( self.params, self.data_dock, features[0], closest_junction_ft, self.snapped_vertex, self.params.pumps_vlay, { Pump.field_name_param: pump_param, Pump.field_name_head: pump_head, Pump.field_name_power: pump_power, Pump.field_name_speed: pump_speed, Pump.field_name_speed_pattern: pump_speed_pattern, Pump.field_name_status: pump_status, Pump.field_name_description: pump_desc, Pump.field_name_tag: pump_tag }) if pump_param == Pump.parameters_head and pump_head is None: self.iface.messageBar().pushMessage( Parameters.plug_in_name, 'The pump was added, but with a NULL value.', Qgis.Info, 5) except PumpValveCreationException as ex: self.iface.messageBar().pushMessage( Parameters.plug_in_name, ', '.join(ex.args), Qgis.Info, 5) self.iface.mapCanvas().refresh() elif event.button() == Qt.RightButton: self.mouse_clicked = False # Check whether it clicked on a valve vertex if len( NetworkUtils.find_adjacent_links(self.params, self.snapped_vertex) ['pumps']) == 0 or self.snapped_pipe_id is None: return menu = QMenu() invert_action = menu.addAction( 'Flip orientation') # TODO: softcode action = menu.exec_(self.iface.mapCanvas().mapToGlobal( QPoint(event.pos().x(), event.pos().y()))) if action == invert_action: request = QgsFeatureRequest().setFilterFid( self.snapped_pipe_id) feats = self.params.pipes_vlay.getFeatures(request) features = [feat for feat in feats] if len(features) == 1: adj_links = NetworkUtils.find_links_adjacent_to_link( self.params, self.params.pipes_vlay, features[0], True, False, True) for adj_link in adj_links['pumps']: adj_link_pts = adj_link.geometry().asPolyline() for adj_link_pt in adj_link_pts: if NetworkUtils.points_overlap( adj_link_pt, self.snapped_vertex, self.params.tolerance): geom = adj_link.geometry() if geom.wkbType( ) == QgsWkbTypes.MultiLineString: nodes = geom.asMultiPolyline() for line in nodes: line.reverse() newgeom = QgsGeometry.fromMultiPolyline( nodes) self.params.pumps_vlay.changeGeometry( adj_link.id(), newgeom) if geom.wkbType() == QgsWkbTypes.LineString: nodes = geom.asPolyline() nodes.reverse() newgeom = QgsGeometry.fromPolylineXY(nodes) self.params.pumps_vlay.changeGeometry( adj_link.id(), newgeom) self.iface.mapCanvas().refresh() break def activate(self): # snap_layer_pipes = NetworkUtils.set_up_snap_layer(self.params.pipes_vlay, None, QgsSnapper.SnapToVertexAndSegment) self.update_snapper() # Editing if not self.params.junctions_vlay.isEditable(): self.params.junctions_vlay.startEditing() if not self.params.pipes_vlay.isEditable(): self.params.pipes_vlay.startEditing() if not self.params.pumps_vlay.isEditable(): self.params.pumps_vlay.startEditing() def deactivate(self): # QgsProject.instance().setSnapSettingsForLayer(self.params.pipes_vlay.id(), # True, # QgsSnapper.SnapToSegment, # QgsTolerance.MapUnits, # 0, # True) self.data_dock.btn_add_pump.setChecked(False) self.canvas().scene().removeItem(self.vertex_marker) def isZoomTool(self): return False def isTransient(self): return False def isEditTool(self): return True def reset_marker(self): self.outlet_marker.hide() self.canvas().scene().removeItem(self.outlet_marker) def update_snapper(self): layers = {self.params.pipes_vlay: QgsSnappingConfig.VertexAndSegment} self.snapper = NetworkUtils.set_up_snapper(layers, self.iface.mapCanvas(), self.params.snap_tolerance) self.snapper.toggleEnabled() # Needed by Observable def update(self, observable): self.update_snapper()
class Coordinator(): """YACUP plugin""" def __init__(self, iface, mainWindow=None): """Constructor. :param iface: An interface instance that will be passed to this class which provides the hook by which you can manipulate the QGIS application at run time. :type iface: QgsInterface """ # store references to important stuff from QGIS self.iface = iface self.canvas = iface.mapCanvas() self._project = QgsProject.instance() self._observingLayer = None self._uiHook = mainWindow if isinstance(mainWindow, QMainWindow) else iface # region: LOCALE - UNUSED # initialize plugin directory self.plugin_dir = os.path.dirname(__file__) # #initialize locale localeString = QSettings().value('locale/userLocale') if (localeString): locale = localeString[0:2] else: locale = QLocale().language() locale_path = os.path.join(self.plugin_dir, 'i18n', 'coordinator_{}.qm'.format(locale)) if os.path.exists(locale_path): self.translator = QTranslator() self.translator.load(locale_path) QCoreApplication.installTranslator(self.translator) # endregion # plugin housekeeping: self.openPanelAction = None self.pluginIsActive = False self.dockwidget = None # Init CRS Transformation: self._inputCrs = None self._outputCrs = None # self._transform : input -> output transformation # self._canvasTransform: input -> canvas transformation self.__initTransformers() # initialize canvas marker icon: self.marker = QgsVertexMarker(self.canvas) self.marker.hide() self.marker.setColor(QColor(255, 0, 0)) self.marker.setIconSize(14) self.marker.setIconType( QgsVertexMarker.ICON_CIRCLE ) # See the enum IconType from http://www.qgis.org/api/classQgsVertexMarker.html self.marker.setPenWidth(3) # init point picker: self.mapTool = QgsMapToolEmitPoint(self.canvas) # LIFECYCLE : def initGui(self): """Create the menu entry inside the QGIS GUI.""" icon = QIcon(':/plugins/coordinator/icons/marker.svg') menuTitle = CT.tr("Open Coordinator") self.openPanelAction = QAction(icon, menuTitle) self.openPanelAction.triggered.connect(self.run) self.iface.pluginMenu().addAction(self.openPanelAction) #-------------------------------------------------------------------------- def onClosePlugin(self): """Cleanup necessary items here when plugin dockwidget is closed""" # remove the marker from the canvas: self.marker.hide() self.dockwidget.closingPlugin.disconnect(self.onClosePlugin) # disconnect from the GUI-signals self._disconnectExternalSignals() self.pluginIsActive = False def unload(self): """Removes the plugin menu item from QGIS GUI.""" self.iface.pluginMenu().removeAction(self.openPanelAction) self.marker.hide() self._disconnectExternalSignals() #-------------------------------------------------------------------------- # PRIVATE HELPERS: def __initTransformers(self): """Initializes both coordinate transform object needed: the transformation from the input CRS to the output CRS (self._transform) and a tranformer from the input CRS to the current canvas CRS.""" inputCrs = self._inputCrs if self._inputCrs != None else QgsCoordinateReferenceSystem( "EPSG:4326") outputCrs = self._outputCrs if self._outputCrs != None else QgsCoordinateReferenceSystem( "EPSG:4326") self._transform = QgsCoordinateTransform(inputCrs, outputCrs, self._project) canvasCrs = self.canvas.mapSettings().destinationCrs() # coordinatorLog("init canvas transform with %s -> %s" # % (inputCrs.authid(), canvasCrs.authid() if canvasCrs else "NONE!!") # ) self._canvasTransform = QgsCoordinateTransform(inputCrs, canvasCrs, self._project) def __checkEnableAddFeatureButton(self): shouldEnable = bool(self.dockwidget.hasInput() and self.__compatibleMapTool()) return shouldEnable def __compatibleMapTool(self): newTool = self.canvas.mapTool() if (newTool != None and (newTool.flags() & QgsMapTool.EditTool)): newTool = sip.cast(newTool, QgsMapToolCapture) # pre-check probably fixing issue #3 regarding Enums with Qt >= 5.11: # --> https://www.riverbankcomputing.com/static/Docs/PyQt5/gotchas.html#enums try: validModes = (QgsMapToolCapture.CaptureMode.CapturePoint, QgsMapToolCapture.CaptureMode.CapturePolygon, QgsMapToolCapture.CaptureMode.CaptureLine) except AttributeError: validModes = (QgsMapToolCapture.CapturePoint, QgsMapToolCapture.CapturePolygon, QgsMapToolCapture.CaptureLine) if newTool.mode() in validModes: return newTool return False def _disconnectExternalSignals(self): try: self.iface.mapCanvas().destinationCrsChanged.disconnect( self.mapCanvasCrsChanged) except TypeError: pass try: self.canvas.mapToolSet.disconnect(self.mapToolChanged) except TypeError: pass try: self.iface.currentLayerChanged.disconnect(self.currentLayerChanged) except TypeError: pass try: self.iface.projectRead.disconnect(self.projectRead) except TypeError: pass if (self._observingLayer != None) and sip.isdeleted( self._observingLayer): self._observingLayer = None if self._observingLayer: try: self._observingLayer.crsChanged.disconnect( self.layerChangedCrs) except TypeError: pass def _currentEffectiveCrsInMap(self): activeLayer = self.iface.activeLayer() if (activeLayer): if type(activeLayer) == QgsVectorLayer: return activeLayer.sourceCrs() elif type(activeLayer) == QgsRasterLayer: return activeLayer.crs() else: return self.canvas.mapSettings().destinationCrs() def _warnIfPointOutsideCanvas(self): if (self.dockwidget.hasInput() and not self.canvas.extent().contains( self.inputCoordinatesInCanvasCrs())): self.setWarningMessage(CT.tr("outside of map extent")) else: self.setWarningMessage(None) #-------------------------------------------------------------------------- # GETTERS/SETTERS def setInputCrs(self, crs): oldCrs = self._inputCrs if (oldCrs == None) or (crs.authid() != oldCrs.authid()): #coordinatorLog("setting input CRS to %s" % crs.authid()) currentCoordinates = self.dockwidget.inputCoordinates() self._inputCrs = crs self.dockwidget.setSectionCrs(CoordinatorDockWidget.SectionInput, crs) self._transform.setSourceCrs(crs) self._canvasTransform.setSourceCrs(crs) if (oldCrs != None and self.dockwidget.hasInput()): # make sure we transform the currently set coordinate # to the new Coordinate System : t = QgsCoordinateTransform(oldCrs, crs, QgsProject.instance()) transformedPoint = t.transform( QgsPointXY(currentCoordinates[0], currentCoordinates[1])) self.dockwidget.setInputPoint(transformedPoint) else: self.dockwidget.clearSection(CoordinatorDockWidget.SectionBoth) def inputCrs(self): return self._inputCrs def setOutputCrs(self, crs): #coordinatorLog("changing output CRS %s -> %s" % (self._outputCrs.authid() if self._outputCrs else "NONE!", crs.authid()) ) oldCrs = self._outputCrs if (oldCrs == None) or (oldCrs.authid() != crs.authid()): self._outputCrs = crs self.dockwidget.setSectionCrs(CoordinatorDockWidget.SectionOutput, crs) self._transform.setDestinationCrs(crs) self.process() def outputCrs(self): return self._outputCrs def setWarningMessage(self, message): self.dockwidget.setWarningMessage(message) # ACTIONS : def openCrsSelectionDialogForSection(self, section): projSelector = QgsProjectionSelectionDialog() if (projSelector.exec()): if section == CoordinatorDockWidget.SectionInput: self.setInputCrs(projSelector.crs()) elif section == CoordinatorDockWidget.SectionOutput: self.setOutputCrs(projSelector.crs()) def setOutputCrsToCanvasCrs(self): crs = self.canvas.mapSettings().destinationCrs() self.setOutputCrs(crs) def connectCrsToCanvas(self, section, connect): #coordinatorLog("%s %s" % (section, connect)) if section == CoordinatorDockWidget.SectionOutput: if (connect): # connect to map # disable dialog for selecting output CRS: self.dockwidget.outputCrs.clicked.disconnect() # set CRS to be canvas' CRS and follow it self.setOutputCrsToCanvasCrs() else: # enable dialog selection self.dockwidget.outputCrs.clicked.connect( partial(self.openCrsSelectionDialogForSection, CoordinatorDockWidget.SectionOutput)) def addCurrentCoordinatesToDigitizeSession(self): editTool = self.__compatibleMapTool() if (not editTool): return False result = False point = self.inputCoordinatesInCanvasCrs() if (editTool.mode() == QgsMapToolCapture.CaptureMode.CapturePoint): #coordinatorLog("Point Capture!") layer = self.iface.activeLayer() geometry = QgsGeometry.fromPointXY(point) feature = QgsVectorLayerUtils.createFeature( layer, geometry, {}, layer.createExpressionContext()) if ((len(layer.fields()) < 1) or self.iface.openFeatureForm(layer, feature)): result = layer.addFeature(feature) if (result): #coordinatorLog("Point successfully written to layer") pass layer.triggerRepaint() elif ((editTool.mode() == QgsMapToolCapture.CaptureMode.CapturePolygon) or (editTool.mode() == QgsMapToolCapture.CaptureMode.CaptureLine)): #coordinatorLog("Rubberband Capture!") result = (editTool.addVertex(point) == 0) else: return False if (result): self.dockwidget.showInfoMessage(CT.tr("coordinate added"), 1500) else: self.setWarningMessage(CT.tr("adding coordinate failed")) return result def switchInputOutputCrs(self): inputCrs = self._inputCrs self.setInputCrs(self._outputCrs) self.setOutputCrs(inputCrs) if self.dockwidget.outputCrsConn.isChecked() and ( self._outputCrs.authid() != self._inputCrs.authid()): self.dockwidget.outputCrsConn.setChecked(False) def _showMarker(self, show): if show and self.dockwidget.hasInput(): self.marker.show() else: self.marker.hide() # API : def enableMarker(self, show): self._showMarker(show) self.dockwidget.showMarker.setChecked(show) # PIPELINE : def ensureValidInputGui(self): self.dockwidget.addFeatureButton.setEnabled( self.__checkEnableAddFeatureButton()) self._warnIfPointOutsideCanvas() # make sure we show the marker now: if self.dockwidget.hasInput(): if self.dockwidget.showMarker.isChecked(): self.marker.show() else: self.marker.hide() self.dockwidget.setInputToDMS(self.dockwidget.inputAsDMS.isChecked() & self._inputCrs.isGeographic()) def process(self): #coordinatorLog("about to process input") if (self.dockwidget.hasInput()): (x, y) = self.dockwidget.inputCoordinates() # coordinatorLog("Input: %f %f " % (x, y) ) transformedPoint = self._transform.transform(QgsPointXY(x, y)) # coordinatorLog("Transformed point: %f %f " % (transformedPoint.x(), transformedPoint.y()) ) self.dockwidget.setResultPoint(transformedPoint) self.marker.setCenter(self.inputCoordinatesInCanvasCrs()) else: self.dockwidget.clearFieldsInSection( CoordinatorDockWidget.SectionOutput) def reset(self): self.__initTransformers() self.dockwidget.resetInterface() self.dockwidget.setEastingInverted(False) self.dockwidget.setNorthingInverted(False) def inputCoordinatesInCanvasCrs(self): (x, y) = self.dockwidget.inputCoordinates() result = self._canvasTransform.transform(QgsPointXY(x, y)) #coordinatorLog("transformation: (%s,%s) are (%s,%s)" % (x,y, result.x(), result.y())) #coordinatorLog(" %s -> %s" % (self._canvasTransform.sourceCrs().authid(), self._canvasTransform.destinationCrs().authid()) ) return result # SLOTS : def mapCanvasCrsChanged(self): self._canvasTransform.setDestinationCrs( self.canvas.mapSettings().destinationCrs()) #if(self.dockwidget.outputCrsConn.isChecked()): # self.setOutputCrs(self.canvas.mapSettings().destinationCrs()) def inputCoordinatesChanged(self): #Coordinator.log("Input changed") self.process() self.ensureValidInputGui() def inputFormatChanged(self): self.ensureValidInputGui() def outputFormatChanged(self): self.process() def moveCanvasButtonClicked(self): self.canvas.setCenter(self.inputCoordinatesInCanvasCrs()) def mapCrsConnectionButtonToggled(self, forSection, enabled): self.connectCrsToCanvas(forSection, enabled) def showMarkerButtonToggled(self, show): self._showMarker(show) def captureCoordsButtonToggled(self, enabled): #coordinatorLog( "enable Capture Coords: %s" % enabled) if (enabled): self.canvas.setMapTool(self.mapTool) self.mapTool.canvasClicked.connect(self.canvasClickedWithPicker) else: try: self.mapTool.canvasClicked.disconnect( self.canvasClickedWithPicker) except TypeError: pass self.canvas.unsetMapTool(self.mapTool) def canvasClickedWithPicker(self, point, button): # button is the MouseButton #coordinatorLog(type(button).__name__) if QApplication.keyboardModifiers() and Qt.ControlModifier: self.setInputCrs(self.canvas.mapSettings().destinationCrs()) # coordinatorLog("Current Canvas Transform is %s -> %s (we do the reverse to get input)" # % (self._canvasTransform.sourceCrs().authid(), self._canvasTransform.destinationCrs().authid()) # ) point = self._canvasTransform.transform( point, QgsCoordinateTransform.ReverseTransform) self.dockwidget.setInputPoint(point) def canvasMoved(self): self._warnIfPointOutsideCanvas() def mapToolChanged(self): #coordinatorLog("map tools changed") #coordinatorLog( type( self.canvas.mapTool() ).__name__ ) currentMapTool = self.canvas.mapTool() if (currentMapTool == self.mapTool): # user selected our coordinate capture tool -> do nothing pass elif (self.__checkEnableAddFeatureButton()): # user selected a tool to modify features -> enable our add feature button self.dockwidget.addFeatureButton.setEnabled(True) self.dockwidget.captureCoordButton.setChecked(False) else: # user selected a totally unrelated tool -> make sure our coordinate capture tool is disabled self.dockwidget.captureCoordButton.setChecked(False) def addFeatureClicked(self): self.addCurrentCoordinatesToDigitizeSession() def projectRead(self): #coordinatorLog("new project") self._project = QgsProject.instance() self.reset() def currentLayerChanged(self, layer): if self._observingLayer: self._observingLayer.crsChanged.disconnect(self.layerChangedCrs) if layer: self._observingLayer = layer self._observingLayer.crsChanged.connect(self.layerChangedCrs) #coordinatorLog("%s" % type(layer).__name__) if self.dockwidget.outputCrsConn.isChecked(): self.setOutputCrs(self._currentEffectiveCrsInMap()) def layerChangedCrs(self): #coordinatorLog("%s" % type(layer).__name__) if self.dockwidget.outputCrsConn.isChecked(): self.setOutputCrs(self._currentEffectiveCrsInMap()) def run(self): """Run method that loads and starts the plugin""" #coordinatorLog("run", "Coordinator") if not self.pluginIsActive: self.pluginIsActive = True #QgsMessageLog.logMessage("Starting", "Coordinator") # dockwidget may not exist if: # first run of plugin # removed on close (see self.onClosePlugin method) if self.dockwidget == None: # Create the dockwidget (after translation) and keep reference self.dockwidget = CoordinatorDockWidget() #for child in self.dockwidget.children(): # self.log("Child: %s" % child.objectName()) # MA LOGIC: # EXTERNAL connections self.canvas.destinationCrsChanged.connect(self.mapCanvasCrsChanged) self.canvas.mapToolSet.connect(self.mapToolChanged) self.canvas.extentsChanged.connect(self.canvasMoved) self.iface.projectRead.connect(self.projectRead) self.iface.currentLayerChanged.connect(self.currentLayerChanged) # CONNECT the initially active buttons from the GUI: self.dockwidget.selectCrsButton.clicked.connect( partial(self.openCrsSelectionDialogForSection, CoordinatorDockWidget.SectionInput)) self.dockwidget.mapConnectionChanged.connect( self.mapCrsConnectionButtonToggled) # set the inital CRS: self.setInputCrs(QgsCoordinateReferenceSystem("EPSG:4326")) self.setOutputCrs(self.canvas.mapSettings().destinationCrs()) # connect the marker button : self.dockwidget.showMarker.clicked.connect( self.showMarkerButtonToggled) self.dockwidget.moveCanvas.clicked.connect( self.moveCanvasButtonClicked) self.dockwidget.captureCoordButton.clicked.connect( self.captureCoordsButtonToggled) self.dockwidget.addFeatureButton.clicked.connect( self.addFeatureClicked) self.dockwidget.inputFormatButtonGroup.buttonClicked.connect( self.inputFormatChanged) self.dockwidget.resultFormatButtonGroup.buttonClicked.connect( self.outputFormatChanged) self.dockwidget.inputChanged.connect(self.inputCoordinatesChanged) # connect to provide cleanup on closing of dockwidget self.dockwidget.closingPlugin.connect(self.onClosePlugin) # SETUP: self.enableMarker(True) self._canvasTransform.setDestinationCrs( self.canvas.mapSettings().destinationCrs()) # show the dockwidget # TODO: fix to allow choice of dock location self._uiHook.addDockWidget(Qt.LeftDockWidgetArea, self.dockwidget) self.dockwidget.show()
# coding: utf-8 from PyQt4.QtGui import QColor from qgis.utils import iface from qgis.core import QgsPoint from qgis.gui import QgsVertexMarker canvas = iface.mapCanvas() m = QgsVertexMarker(canvas) m.setCenter(QgsPoint(0, 0)) m.setColor(QColor(0, 0, 255)) m.setIconSize(7) m.setIconType( QgsVertexMarker.ICON_BOX ) # See the enum IconType from http://www.qgis.org/api/classQgsVertexMarker.html m.setPenWidth(3) m.hide() m.show() # Remove the element # canvas.scene().removeItem(m)
class addPolygon(QgsMapTool): cut = pyqtSignal() deact = pyqtSignal() def __init__(self, iface, action, geometryClass): self.canvas = iface.mapCanvas() self.iface = iface self.action = action self.geometryClass = geometryClass obj_color = QColor(254, 0, 0) obj_color_alpha = QColor(254, 0, 0) obj_color_alpha.setAlpha(60) vert_color = QColor(0, 0, 255) QgsMapTool.__init__(self, self.canvas) self.rubberBand = QgsRubberBand(self.canvas, QgsWkbTypes.GeometryType(3)) self.rubberBand.setWidth(1) self.rubberBand.setStrokeColor(obj_color) self.rubberBand.setFillColor(obj_color_alpha) self.rubberBand_click = QgsRubberBand(self.canvas, QgsWkbTypes.GeometryType(3)) self.rubberBand_click.setWidth(0) self.rubberBand_click.setFillColor(obj_color_alpha) # snap marker self.snap_mark = QgsVertexMarker(self.canvas) self.snap_mark.setColor(vert_color) self.snap_mark.setPenWidth(2) self.snap_mark.setIconType(QgsVertexMarker.ICON_BOX) self.snap_mark.setIconSize(10) self.points = [] def activate(self): self.action.setChecked(True) self.setCursor(Qt.CrossCursor) def canvasMoveEvent(self, e): self.snap_mark.hide() self.snapPoint = False self.snapPoint = self.checkSnapToPoint(e.pos()) if self.snapPoint[0]: self.snap_mark.setCenter(self.snapPoint[1]) self.snap_mark.show() if len(self.points) > 0: self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) point = self.toMapCoordinates(self.canvas.mouseLastXY()) temp_points = self.points[:] temp_points.append(point) polygon = QgsGeometry.fromPolygonXY([temp_points]) self.rubberBand.setToGeometry(polygon, None) self.rubberBand.show() def canvasPressEvent(self, e): # Left mouse button if e.button() == Qt.LeftButton: if self.snapPoint[0]: point = self.snapPoint[1] else: point = self.toMapCoordinates(self.canvas.mouseLastXY()) self.points.append(point) polygon = QgsGeometry.fromPolygonXY([self.points]) self.rubberBand_click.reset(QgsWkbTypes.GeometryType(3)) self.rubberBand_click.setToGeometry(polygon, None) self.rubberBand_click.show() # Right mouse button if e.button() == Qt.RightButton: geometry = QgsGeometry.fromPolygonXY([self.points]) self.geometryClass.geometry = geometry self.cut.emit() self.reset() def checkSnapToPoint(self, point): snapped = False snap_point = self.toMapCoordinates(point) snapper = self.canvas.snappingUtils() snapMatch = snapper.snapToMap(point) if snapMatch.hasVertex(): snap_point = snapMatch.point() snapped = True return snapped, snap_point def deactivate(self): self.action.setChecked(False) self.reset() self.deact.emit() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.reset() def reset(self): self.rubberBand_click.reset(QgsWkbTypes.GeometryType(3)) self.rubberBand.reset(QgsWkbTypes.GeometryType(3)) self.snap_mark.hide() self.points = []
class ConnecMapTool(ParentMapTool): """ Button 20. User select connections from layer 'connec' Execute SQL function: 'gw_fct_connect_to_network' """ def __init__(self, iface, settings, action, index_action): """ Class constructor """ # Call ParentMapTool constructor super(ConnecMapTool, self).__init__(iface, settings, action, index_action) self.dragging = False # Vertex marker self.vertexMarker = QgsVertexMarker(self.canvas) self.vertexMarker.setColor(QColor(255, 25, 25)) self.vertexMarker.setIconSize(11) self.vertexMarker.setIconType(QgsVertexMarker.ICON_BOX) # or ICON_CROSS, ICON_X self.vertexMarker.setPenWidth(5) # Rubber band self.rubberBand = QgsRubberBand(self.canvas, True) mFillColor = QColor(100, 0, 0) self.rubberBand.setColor(mFillColor) self.rubberBand.setWidth(3) mBorderColor = QColor(254, 58, 29) self.rubberBand.setBorderColor(mBorderColor) # Select rectangle self.selectRect = QRect() def reset(self): """ Clear selected features """ layer = self.layer_connec if layer is not None: layer.removeSelection() # Graphic elements self.rubberBand.reset() """ QgsMapTools inherited event functions """ def canvasMoveEvent(self, event): """ With left click the digitizing is finished """ if event.buttons() == Qt.LeftButton: if not self.dragging: self.dragging = True self.selectRect.setTopLeft(event.pos()) self.selectRect.setBottomRight(event.pos()) self.set_rubber_band() else: # Hide highlight self.vertexMarker.hide() # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Snapping (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> []: # Check Arc or Node for snapPoint in result: if snapPoint.layer == self.layer_connec: # Get the point point = QgsPoint(result[0].snappedVertex) # Add marker self.vertexMarker.setCenter(point) self.vertexMarker.show() break def canvasPressEvent(self, event): self.selectRect.setRect(0, 0, 0, 0) self.rubberBand.reset() def canvasReleaseEvent(self, event): """ With left click the digitizing is finished """ if event.button() == Qt.LeftButton: # Get the click x = event.pos().x() y = event.pos().y() eventPoint = QPoint(x, y) # Node layer layer = self.layer_connec # Not dragging, just simple selection if not self.dragging: # Snap to node (retval, result) = self.snapper.snapToBackgroundLayers(eventPoint) # @UnusedVariable # That's the snapped point if result <> [] and (result[0].layer.name() == self.layer_connec.name()): point = QgsPoint(result[0].snappedVertex) # @UnusedVariable layer.removeSelection() layer.select([result[0].snappedAtGeometry]) # Create link self.link_connec() # Hide highlight self.vertexMarker.hide() else: # Set valid values for rectangle's width and height if self.selectRect.width() == 1: self.selectRect.setLeft(self.selectRect.left() + 1) if self.selectRect.height() == 1: self.selectRect.setBottom(self.selectRect.bottom() + 1) self.set_rubber_band() selectGeom = self.rubberBand.asGeometry() # @UnusedVariable self.select_multiple_features(self.selectRectMapCoord) self.dragging = False # Create link self.link_connec() elif event.button() == Qt.RightButton: # Create link self.link_connec() def activate(self): # Check button self.action().setChecked(True) # Rubber band self.rubberBand.reset() # Store user snapping configuration self.snapperManager.storeSnappingOptions() # Clear snapping self.snapperManager.clearSnapping() # Set snapping to arc and node self.snapperManager.snapToConnec() # Change cursor self.canvas.setCursor(self.cursor) # Show help message when action is activated if self.show_help: message = ( "Right click to use current selection, select connec points by clicking or dragging (selection box)" ) self.controller.show_info(message, context_name="ui_message") # Control current layer (due to QGIS bug in snapping system) try: if self.canvas.currentLayer().type() == QgsMapLayer.VectorLayer: self.canvas.setCurrentLayer(self.layer_connec) except: self.canvas.setCurrentLayer(self.layer_connec) def deactivate(self): # Check button self.action().setChecked(False) # Rubber band self.rubberBand.reset() # Restore previous snapping self.snapperManager.recoverSnappingOptions() # Recover cursor self.canvas.setCursor(self.stdCursor) def link_connec(self): """ Link selected connec to the pipe """ # Get selected features (from layer 'connec') aux = "{" layer = self.layer_connec if layer.selectedFeatureCount() == 0: message = "You have to select at least one feature!" self.controller.show_warning(message, context_name="ui_message") return features = layer.selectedFeatures() for feature in features: connec_id = feature.attribute("connec_id") aux += str(connec_id) + ", " connec_array = aux[:-2] + "}" # Execute function function_name = "gw_fct_connect_to_network" sql = "SELECT " + self.schema_name + "." + function_name + "('" + connec_array + "');" self.controller.execute_sql(sql) # Refresh map canvas self.rubberBand.reset() self.iface.mapCanvas().refresh() def set_rubber_band(self): # Coordinates transform transform = self.canvas.getCoordinateTransform() # Coordinates ll = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.bottom()) lr = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.bottom()) ul = transform.toMapCoordinates(self.selectRect.left(), self.selectRect.top()) ur = transform.toMapCoordinates(self.selectRect.right(), self.selectRect.top()) # Rubber band self.rubberBand.reset() self.rubberBand.addPoint(ll, False) self.rubberBand.addPoint(lr, False) self.rubberBand.addPoint(ur, False) self.rubberBand.addPoint(ul, False) self.rubberBand.addPoint(ll, True) self.selectRectMapCoord = QgsRectangle(ll, ur) def select_multiple_features(self, selectGeometry): # Default choice behaviour = QgsVectorLayer.SetSelection # Modifiers modifiers = QApplication.keyboardModifiers() if modifiers == Qt.ControlModifier: behaviour = QgsVectorLayer.AddToSelection elif modifiers == Qt.ShiftModifier: behaviour = QgsVectorLayer.RemoveFromSelection if self.layer_connec is None: return # Change cursor QApplication.setOverrideCursor(Qt.WaitCursor) # Selection self.layer_connec.selectByRect(selectGeometry, behaviour) # Old cursor QApplication.restoreOverrideCursor()