class Window(QMainWindow, Ui_MainWindow): def __init__(self): super(Window, self).__init__() self.setupUi(self) self.layers = [] # ---------初始化图层和画布---------- self.vl = QVBoxLayout(self.dockWidgetContents) self.layer_tree_view = QgsLayerTreeView(self) self.vl.addWidget(self.layer_tree_view) self.hl = QHBoxLayout(self.frame) self.mapCanvas = QgsMapCanvas(self.frame) self.hl.addWidget(self.mapCanvas) # ---------初始化图层和画布---------- self.action_open.triggered.connect(self.action_open_triggered) # 建立桥梁 self.model = QgsLayerTreeModel(QgsProject.instance().layerTreeRoot(), self) self.model.setFlag(QgsLayerTreeModel.AllowNodeRename) self.model.setFlag(QgsLayerTreeModel.AllowNodeReorder) self.model.setFlag(QgsLayerTreeModel.AllowNodeChangeVisibility) self.model.setFlag(QgsLayerTreeModel.ShowLegendAsTree) self.model.setAutoCollapseLegendNodes(10) self.layer_tree_view.setModel(self.model) self.layer_tree_bridge = QgsLayerTreeMapCanvasBridge(QgsProject.instance().layerTreeRoot(), self.mapCanvas) # 设置识别工具 self.identifyTool = QgsMapToolIdentifyFeature(self.mapCanvas) # 发送识别的要素 self.identifyTool.featureIdentified.connect(self.print_features) self.mapCanvas.setMapTool(self.identifyTool) def action_open_triggered(self): data_file, ext = QFileDialog.getOpenFileName(self, '打开', '', '所有文件(*)') if data_file: if data_file.endswith('.shp'): basename = os.path.splitext(os.path.basename(data_file))[0] layer = QgsVectorLayer(data_file, basename, "ogr") QgsProject.instance().addMapLayer(layer) self.layers.append(layer) self.mapCanvas.setExtent(layer.extent()) self.mapCanvas.setLayers(self.layers) self.mapCanvas.refresh() self.layer_tree_view.setCurrentLayer(layer) # 设置需要识别的图层 self.identifyTool.setLayer(layer) elif data_file.endswith('.qgz') or data_file.endswith('.qgs'): QgsProject.instance().read(data_file) else: print('error') def print_features(self, feature): print(feature.attributes()) print(feature.fields()) print(feature.geometry())
class MapWindow(QMainWindow): """This class offers a canvas and tools to select polygons from a vector layer provided by the main app.""" # signal emitted when polygons succesfully selected finished = pyqtSignal() def __init__(self): QMainWindow.__init__(self) #self.setWindowFlags(Qt.CustomizeWindowHint) #self.setWindowFlags(Qt.WindowMinMaxButtonsHint) # creating map canvas, which draws the maplayers # setting up features like canvas color self.canvas = QgsMapCanvas() self.canvas.setMinimumSize(550, 700) self.canvas.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.canvas.setCanvasColor(Qt.white) self.canvas.setSelectionColor(QColor(255,255,26,200)) self.canvas.enableAntiAliasing(True) self.canvas.setParallelRenderingEnabled(True) # empty list for selected polygons self.selected_features = [] # setting up label settings: object below houses all of them self.label_settings = QgsPalLayerSettings() # object for text settings text_format = QgsTextFormat() text_format.setFont(QFont("Helvetica", 12)) text_format.setSize(7) # setting up a white buffer around the labels buffer_settings = QgsTextBufferSettings() buffer_settings.setEnabled(True) buffer_settings.setSize(0.65) buffer_settings.setColor(Qt.white) text_format.setBuffer(buffer_settings) # label settings: # fieldName = which field is shown as the label (currently Finnish name) # placement = labels can be placed differently in relation to one another # - see documentation for details self.label_settings.setFormat(text_format) self.label_settings.fieldName = "namefin" self.label_settings.placement = 0 self.label_settings.enabled = True # Qmainwindow requires a central widget. Canvas is placed self.setCentralWidget(self.canvas) # creating each desired action self.actionGet = QAction("Return selected and close", self) self.actionPan = QAction("Pan tool", self) self.actionSelect = QAction("Select tool", self) self.actionClear = QAction("Clear selection", self) self.actionCancel = QAction("Cancel and close", self) # these two function as on/off. the rest are clickable self.actionPan.setCheckable(True) self.actionSelect.setCheckable(True) # when actions are clicked, do corresponding function self.actionPan.triggered.connect(self.pan) self.actionSelect.triggered.connect(self.select) self.actionClear.triggered.connect(self.clearSelection) self.actionGet.triggered.connect(self.finishedSelection) self.actionCancel.triggered.connect(self.cancel) # toolbar at the top of the screen: houses actions as buttons # change order here to change their placement on window self.toolbar = self.addToolBar("Canvas actions") self.toolbar.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbar.setMovable(False) self.toolbar.addAction(self.actionGet) self.toolbar.addAction(self.actionPan) self.toolbar.addAction(self.actionSelect) self.toolbar.addAction(self.actionClear) self.toolbar.addAction(self.actionCancel) # link actions to premade map tools self.toolPan = QgsMapToolPan(self.canvas) self.toolPan.setAction(self.actionPan) self.toolSelect = QgsMapToolIdentifyFeature(self.canvas) self.toolSelect.setAction(self.actionSelect) self.toolSelect.featureIdentified.connect(self.selectFeature) self.blocks_flag = False self.selection_rectangle = False # set select tool as default self.select() def pan(self): """Simply activates the tool""" self.canvas.setMapTool(self.toolPan) def select(self): self.canvas.setMapTool(self.toolSelect) def addLayer(self, layer): """Called when user click button on the main plugin: receives a vector layer, sets up labels & rendering parameters and shows the layer.""" # empty output list in case function is called multiple times self.selected_features.clear() self.selection_rectangle = False # layer into a self variable self.layer = layer # add layer to project: required to show it on screen # False = do not show the layer on the legend listing nor draw on main canvas QgsProject.instance().addMapLayer(self.layer, False) # set up visual stuff self.layer.setLabelsEnabled(True) layer_labeling = QgsVectorLayerSimpleLabeling(self.label_settings) self.layer.setLabeling(layer_labeling) self.layer.renderer().symbol().setColor(QColor(220,220,220)) # select tool needs a vector layer assigned to it self.toolSelect.setLayer(self.layer) self.canvas.setExtent(self.layer.extent()) # set layer to canvas self.canvas.setLayers([self.layer]) # show to user self.show() def addBlocksLayer(self, layer): self.selected_features.clear() self.blocks_flag = True self.layer = layer QgsProject.instance().addMapLayer(self.layer, False) self.layer.renderer().symbol().setColor(Qt.cyan) self.layer.renderer().symbol().setOpacity(0.30) # select tool needs a vector layer assigned to it self.toolSelect.setLayer(self.layer) self.canvas.setExtent(self.layer.extent()) # set layer to canvas """ url = ("https://vm0160.kaj.pouta.csc.fi/geoserver/ows?service=wfs&version=2.0.0"+ "&request=GetFeature&typename=ogiir:maakuntajako_2018_4500k&pagingEnabled=true") self.bg_layer = QgsVectorLayer(url, "BACKGROUND-REMOVE", "WFS") """ self.bg_layer = QgsRasterLayer("url=https://vm0160.kaj.pouta.csc.fi/ogiir_cache/wmts/1.0.0/" + "WMTSCapabilities.xml&crs=EPSG:3067&dpiMode=7&format=image/"+ "png&layers=taustakartta&styles=default&tileMatrixSet=GRIDI-FIN", 'GEOCUBES BG-LAYER - TEMPORARY', 'wms') if self.bg_layer.isValid(): QgsProject.instance().addMapLayer(self.bg_layer, False) self.canvas.setLayers([self.layer, self.bg_layer]) else: self.canvas.setLayers([self.layer]) self.show() def selectFeature(self, feat): """Activated when user clicks something on screen. This returns the clicked feature. The function does 2 things: 1. selects the feature on the map / deselects if already selected 2. adds features to a list in the same format (name, id_code) as they're stored in the 'Admin areas box' in the main file """ idx = feat.id() if self.blocks_flag: xmin = feat[0] ymax = feat[1] label = str(xmin) + "|" + str(ymax) else: code = feat[1] name = feat[2] label = name + "|" + code if label in self.selected_features: self.layer.deselect(idx) self.selected_features.remove(label) else: self.layer.select(idx) self.selected_features.append(label) def clearSelection(self): """Clear map selection and list on button click""" self.layer.removeSelection() self.selected_features.clear() def finishedSelection(self): """Activated when user clicks 'return selection'. Closes window and emits signal to indicate the job is finished""" self.close() self.finished.emit() def cancel(self): """In case user changes their mind. Does the same as above, but doesn't emit signal.""" self.close() def getSelection(self): """Returns list of selected features (polygons)""" return self.selected_features def getSelectionBbox(self): return self.selection_rectangle def closeEvent(self, event): """Activated anytime Mapwindow is closed either by buttons given or if the user finds some other way to close the window. Removes selection and deletes scrap maplayer.""" self.selection_rectangle = self.layer.boundingBoxOfSelected() self.layer.removeSelection() QgsProject.instance().removeMapLayer(self.layer) try: QgsProject.instance().removeMapLayer(self.bg_layer) except Exception: pass self.blocks_flag = False QMainWindow.closeEvent(self, event)
class LinkerDock(QDockWidget, Ui_linker, SettingDialog): def __init__(self, iface): # QGIS self.iface = iface self.settings = MySettings() self.linkRubber = QgsRubberBand(self.iface.mapCanvas()) self.featureHighlight = None # Relation management self.relationManager = QgsProject.instance().relationManager() self.relationManager.changed.connect(self.loadRelations) self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationWidgetWrapper = None self.editorContext = QgsAttributeEditorContext() self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools()) # GUI QDockWidget.__init__(self) self.setupUi(self) SettingDialog.__init__(self, MySettings(), False, True) self.drawButton.setChecked(self.settings.value("drawEnabled")) self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas()) self.mapTool.setButton(self.identifyReferencingFeatureButton) # Connect signal/slot self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged) self.mapTool.featureIdentified.connect(self.setReferencingFeature) # load relations at start self.loadRelations() def showEvent(self, QShowEvent): self.drawLink() def closeEvent(self, e): self.iface.mapCanvas().unsetMapTool(self.mapTool) self.linkRubber.reset() self.deleteHighlight() self.deleteWrapper() self.disconnectLayer() def disconnectLayer(self): if self.relation.isValid(): self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside) def runForFeature(self, relationId, layer, feature): index = self.relationComboBox.findData(relationId) self.relationComboBox.setCurrentIndex(index) self.setReferencingFeature(feature) self.show() if not layer.isEditable(): self.iface.messageBar().pushMessage("Link It", "Cannot set a new related feature since %s is not editable" % layer.name(), QgsMessageBar.WARNING, 4) else: self.relationReferenceWidget.mapIdentification() @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked") def activateMapTool(self): self.iface.mapCanvas().setMapTool(self.mapTool) def deactivateMapTool(self): self.iface.mapCanvas().unsetMapTool(self.mapTool) def loadRelations(self): self.deleteWrapper() self.disconnectLayer() self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationComboBox.currentIndexChanged.disconnect(self.currentRelationChanged) self.relationComboBox.clear() for relation in self.relationManager.referencedRelations(): if relation.referencingLayer().hasGeometryType(): self.relationComboBox.addItem(relation.name(), relation.id()) self.relationComboBox.setCurrentIndex(-1) self.relationComboBox.currentIndexChanged.connect(self.currentRelationChanged) self.currentRelationChanged(-1) def currentRelationChanged(self, index): # disconnect previous relation if self.relation.isValid(): try: self.relation.referencingLayer().editingStarted.disconnect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.disconnect(self.layerValueChangedOutside) except TypeError: pass self.referencingFeatureLayout.setEnabled(index >= 0) relationId = self.relationComboBox.itemData(index) self.relation = self.relationManager.relation(relationId) self.mapTool.setLayer(self.relation.referencingLayer()) self.setReferencingFeature() # connect if self.relation.isValid(): self.relation.referencingLayer().editingStarted.connect(self.relationEditableChanged) self.relation.referencingLayer().editingStopped.connect(self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.connect(self.layerValueChangedOutside) def setReferencingFeature(self, feature=QgsFeature()): self.deactivateMapTool() self.referencingFeature = QgsFeature(feature) self.deleteWrapper() # disable relation reference widget if no referencing feature self.referencedFeatureLayout.setEnabled(feature.isValid()) # set line edit if not self.relation.isValid() or not feature.isValid(): self.referencingFeatureLineEdit.clear() return self.referencingFeatureLineEdit.setText("%s" % feature.id()) fieldIdx = self.referencingFieldIndex() widgetConfig = self.relation.referencingLayer().editorWidgetV2Config(fieldIdx) self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create("RelationReference", self.relation.referencingLayer(), fieldIdx, widgetConfig, self.relationReferenceWidget, self, self.editorContext) self.relationWidgetWrapper.setEnabled(self.relation.referencingLayer().isEditable()) self.relationWidgetWrapper.setValue(feature[fieldIdx]) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) # override field definition to allow map identification self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) # update drawn link self.highlightReferencingFeature() self.drawLink() def deleteWrapper(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged) self.relationWidgetWrapper.setValue(None) del self.relationWidgetWrapper self.relationWidgetWrapper = None def foreignKeyChanged(self, newKey): if not self.relation.isValid() or not self.relation.referencingLayer().isEditable() or not self.referencingFeature.isValid(): self.drawLink() return if not self.relation.referencingLayer().editBuffer().changeAttributeValue(self.referencingFeature.id(), self.referencingFieldIndex(), newKey): self.iface.messageBar().pushMessage("Link It", "Cannot change attribute value.", QgsMessageBar.CRITICAL) self.drawLink() def relationEditableChanged(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.setEnabled(self.relation.isValid() and self.relation.referencingLayer().isEditable()) def layerValueChangedOutside(self, fid, fieldIdx, value): if not self.relation.isValid() or not self.referencingFeature.isValid() or self.relationWidgetWrapper is None: return # not the correct feature if fid != self.referencingFeature.id(): return # not the correct field if fieldIdx != self.referencingFieldIndex(): return # widget already has this value if value == self.relationWidgetWrapper.value(): return self.relationWidgetWrapper.valueChanged.disconnect(self.foreignKeyChanged) self.relationWidgetWrapper.setValue(value) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) def referencingFieldIndex(self): if not self.relation.isValid(): return -1 fieldName = self.relation.fieldPairs().keys()[0] fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName) return fieldIdx @pyqtSlot(bool, name="on_drawButton_toggled") def drawLink(self): self.settings.setValue("drawEnabled", self.drawButton.isChecked()) self.linkRubber.reset() if not self.drawButton.isChecked() or not self.referencingFeature.isValid() or not self.relation.isValid(): return referencedFeature = self.relationReferenceWidget.referencedFeature() if not referencedFeature.isValid(): return p1 = self.centroid(self.relation.referencedLayer(), referencedFeature) p2 = self.centroid(self.relation.referencingLayer(), self.referencingFeature) geom = arc(p1, p2) self.linkRubber.setToGeometry(geom, None) self.linkRubber.setWidth(self.settings.value("rubberWidth")) self.linkRubber.setColor(self.settings.value("rubberColor")) self.linkRubber.setLineStyle(Qt.DashLine) def centroid(self, layer, feature): geom = feature.geometry() if geom.type() == QGis.Line: geom = geom.interpolate(geom.length()/2) else: geom = geom.centroid() return self.iface.mapCanvas().mapSettings().layerToMapCoordinates(layer, geom.asPoint()) @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked") def highlightReferencingFeature(self): self.deleteHighlight() if not self.relation.isValid() or not self.referencingFeature.isValid(): return self.featureHighlight = QgsHighlight(self.iface.mapCanvas(), self.referencingFeature.geometry(), self.relation.referencingLayer()) settings = QSettings() color = QColor( settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha())) bbuffer = float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM)) minWidth = float(settings.value("/Map/highlight/minWidth", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.featureHighlight.setColor(color) color.setAlpha(alpha) self.featureHighlight.setFillColor(color) self.featureHighlight.setBuffer(bbuffer) self.featureHighlight.setMinWidth(minWidth) self.featureHighlight.show() timer = QTimer(self) timer.setSingleShot(True) timer.timeout.connect(self.deleteHighlight) timer.start(3000) def deleteHighlight(self): if self.featureHighlight: del self.featureHighlight self.featureHighlight = None
class DockWidgetQueries(QgsDockWidget, DOCKWIDGET_UI): def __init__(self, iface, controller, parent=None): super(DockWidgetQueries, self).__init__(None) self.setupUi(self) self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.iface = iface self._controller = controller self.logger = Logger() self.app = AppInterface() self.canvas = iface.mapCanvas() self.active_map_tool_before_custom = None self._identify_tool = None self._fill_combos() self.btn_identify_plot.setIcon( QIcon(":/Asistente-LADM-COL/resources/images/spatial_unit.png")) self.tab_results.setTabEnabled( TAB_BASIC_INFO_INDEX, False) # TODO: Remove when queries support LevCat 1.2 self.tab_results.setTabEnabled( TAB_PHYSICAL_INFO_INDEX, False) # TODO: Remove when queries support LevCat 1.2 self.tab_results.setTabEnabled( TAB_ECONOMIC_INFO_INDEX, False) # TODO: Remove when queries support LevCat 1.2 self.tab_results.setCurrentIndex( TAB_LEGAL_INFO_INDEX ) # TODO: Remove when queries support LevCat 1.2 # Set connections self._controller.close_view_requested.connect(self._close_dock_widget) self.btn_alphanumeric_query.clicked.connect(self._alphanumeric_query) self.cbo_parcel_fields.currentIndexChanged.connect( self._search_field_updated) self.btn_identify_plot.clicked.connect(self._btn_plot_toggled) self.btn_query_informality.clicked.connect(self._query_informality) self.btn_next_informal_parcel.clicked.connect( self._query_next_informal_parcel) self.btn_previous_informal_parcel.clicked.connect( self._query_previous_informal_parcel) # Context menu self._set_context_menus() # Create maptool self.maptool_identify = QgsMapToolIdentifyFeature(self.canvas) self._initialize_field_values_line_edit() self._update_informal_controls() def _search_field_updated(self, index=None): self._initialize_field_values_line_edit() def _initialize_field_values_line_edit(self): self.txt_alphanumeric_query.setLayer(self._controller.parcel_layer()) idx = self._controller.parcel_layer().fields().indexOf( self.cbo_parcel_fields.currentData()) self.txt_alphanumeric_query.setAttributeIndex(idx) def _set_context_menus(self): self.tree_view_basic.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_basic.customContextMenuRequested.connect( self._show_context_menu) self.tree_view_legal.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_legal.customContextMenuRequested.connect( self._show_context_menu) self.tree_view_physical.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_physical.customContextMenuRequested.connect( self._show_context_menu) self.tree_view_economic.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_economic.customContextMenuRequested.connect( self._show_context_menu) def _close_dock_widget(self): # Deactivate custom tool and close dockwidget self._controller.disconnect_plot_layer() self._controller.disconnect_parcel_layer() self._initialize_tools(new_tool=None, old_tool=self.maptool_identify) self._btn_plot_toggled() self.close( ) # The user needs to use the menus again, which will start everything from scratch def _fill_combos(self): self.cbo_parcel_fields.clear() self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Parcel Number"), self._controller.parcel_number_name()) self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Previous Parcel Number"), self._controller.previous_parcel_number_name()) self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Folio de Matrícula Inmobiliaria"), self._controller.fmi_name()) def _initialize_tools(self, new_tool, old_tool): if self.maptool_identify == old_tool: # custom identify was deactivated try: self.canvas.mapToolSet.disconnect(self._initialize_tools) except TypeError as e: pass self.btn_identify_plot.setChecked(False) else: # custom identify was activated pass def _btn_plot_toggled(self): if self.btn_identify_plot.isChecked(): self._prepare_identify_plot() else: # The button was toggled and deactivated, go back to the previous tool self.canvas.setMapTool(self.active_map_tool_before_custom) def _prepare_identify_plot(self): """ Custom Identify tool was activated, prepare everything for identifying plots """ self.active_map_tool_before_custom = self.canvas.mapTool() self.btn_identify_plot.setChecked(True) self.canvas.mapToolSet.connect(self._initialize_tools) self.maptool_identify.setLayer(self._controller.plot_layer()) cursor = QCursor() cursor.setShape(Qt.PointingHandCursor) self.maptool_identify.setCursor(cursor) self.canvas.setMapTool(self.maptool_identify) try: self.maptool_identify.featureIdentified.disconnect() except TypeError as e: pass self.maptool_identify.featureIdentified.connect( self._search_data_by_plot) def _search_data_by_plot(self, plot_feature): plot_t_id = plot_feature[self._controller.t_id_name()] self.app.gui.flash_features(self._controller.plot_layer(), [plot_feature.id()]) with OverrideCursor(Qt.WaitCursor): if not self.isVisible(): self.show() self._search_data_by_component(plot_t_ids=[plot_t_id], zoom_and_select=False) self._controller.plot_layer().selectByIds([plot_feature.id()]) def _search_data_by_component(self, **kwargs): """ Perform the searches by component and fill tree views :param kwargs: A dict with search criteria. """ self._controller.plot_layer().removeSelection() # Read zoom_and_select parameter and remove it from kwargs bZoom = False if 'zoom_and_select' in kwargs: bZoom = kwargs['zoom_and_select'] del kwargs['zoom_and_select'] if 'parcel_number' in kwargs and kwargs['parcel_number'] == NULL: self.logger.warning( __name__, QCoreApplication.translate( "DockWidgetQueries", "The parcel number is NULL! We cannot retrieve data for parcels with NULL parcel numbers." )) # records = self._controller.search_data_basic_info(**kwargs) # if bZoom: # self._controller.zoom_to_resulting_plots(records) # self._setup_tree_view(self.tree_view_basic, records) records = self._controller.search_data_legal_info(**kwargs) self._setup_tree_view(self.tree_view_legal, records) # records = self._controller.search_data_physical_info(**kwargs) # self._setup_tree_view(self.tree_view_physical, records) # records = self._controller.search_data_economic_info(**kwargs) # self._setup_tree_view(self.tree_view_economic, records) def _setup_tree_view(self, tree_view, records): """ Configure result tree views :param tree_view: Tree view to be updated :param records: List of dicts. A dict per plot: {id: 21, attributes: {...}} """ tree_view.setModel(self._controller.create_model(records)) self._collapse_tree_view_items(tree_view) self._add_thumbnails_to_tree_view(tree_view) def _collapse_tree_view_items(self, tree_view): """ Collapse tree view items based on a property """ tree_view.expandAll() for idx in tree_view.model().getCollapseIndexList(): tree_view.collapse(idx) def _add_thumbnails_to_tree_view(self, tree_view): """ Gets a list of model indexes corresponding to extFiles objects to show a preview """ model = tree_view.model() for idx in model.getPixmapIndexList(): url = model.data(idx, Qt.UserRole)['url'] res, image = self._controller.download_image("{}{}".format( url, SUFFIX_GET_THUMBNAIL)) if res: pixmap = QPixmap() pixmap.loadFromData(image) label = QLabel() label.setPixmap(pixmap) tree_view.setIndexWidget(idx, label) def _alphanumeric_query(self): option = self.cbo_parcel_fields.currentData() query = self.txt_alphanumeric_query.value() if query: if option == self._controller.fmi_name(): self._search_data_by_component(parcel_fmi=query, zoom_and_select=True) elif option == self._controller.parcel_number_name(): self._search_data_by_component(parcel_number=query, zoom_and_select=True) else: # previous_parcel_number self._search_data_by_component(previous_parcel_number=query, zoom_and_select=True) else: self.logger.info_msg( __name__, QCoreApplication.translate("DockWidgetQueries", "First enter a query")) def _show_context_menu(self, point): tree_view = self.sender() index = tree_view.indexAt(point) context_menu = QMenu("Context menu") index_data = index.data(Qt.UserRole) if index_data is None: return if "value" in index_data: action_copy = QAction( QCoreApplication.translate("DockWidgetQueries", "Copy value")) action_copy.triggered.connect( partial(self._controller.copy_value, index_data["value"])) context_menu.addAction(action_copy) context_menu.addSeparator() if "url" in index_data: action_open_url = QAction( QCoreApplication.translate("DockWidgetQueries", "Open URL")) action_open_url.triggered.connect( partial(self._controller.open_url, index_data["url"])) context_menu.addAction(action_open_url) context_menu.addSeparator() # Configure actions for tables/layers if "type" in index_data and "id" in index_data: table_name = index_data["type"] t_id = index_data["id"] if table_name == self._controller.parcel_layer_name(): layer = self._controller.parcel_layer() self.app.core.activate_layer_requested.emit(layer) else: layer = self._controller.get_layer(table_name) if layer is not None: if layer.isSpatial(): action_zoom_to_feature = QAction( QCoreApplication.translate( "DockWidgetQueries", "Zoom to {} with {}={}").format( table_name, self._controller.t_id_name(), t_id)) action_zoom_to_feature.triggered.connect( partial(self._controller.zoom_to_feature, layer, t_id)) context_menu.addAction(action_zoom_to_feature) if table_name == self._controller.parcel_layer_name(): # We show a handy option to zoom to related plots plot_ids = self._controller.get_plots_related_to_parcel( t_id) if plot_ids: action_zoom_to_plots = QAction( QCoreApplication.translate( "DockWidgetQueries", "Zoom to related plot(s)")) action_zoom_to_plots.triggered.connect( partial(self._controller.zoom_to_plots, plot_ids)) context_menu.addAction(action_zoom_to_plots) action_open_feature_form = QAction( QCoreApplication.translate( "DockWidgetQueries", "Open form for {} with {}={}").format( table_name, self._controller.t_id_name(), t_id)) action_open_feature_form.triggered.connect( partial(self._controller.open_feature_form, layer, t_id)) context_menu.addAction(action_open_feature_form) if context_menu.actions(): context_menu.exec_(tree_view.mapToGlobal(point)) def _query_informality(self): first_parcel_number, current, total = self._controller.query_informal_parcels( ) self._search_data_by_component(parcel_number=first_parcel_number, zoom_and_select=True) self._update_informal_controls(first_parcel_number, current, total) if not total: self.logger.info_msg( __name__, QCoreApplication.translate( "DockWidgetQueries", "There are no informal parcels in this database!")) def _update_informal_controls(self, parcel_number='', current=0, total=0): """ Update controls (reset labels, enable buttons if we have informality) """ self._update_informal_labels(parcel_number, current, total) self.btn_query_informality.setText( QCoreApplication.translate("DockWidgetQueries", "Restart" ) if current else QCoreApplication. translate("DockWidgetQueries", "Start")) enable = total > 1 # At least 2 to enable buttons that traverse the parcels self.btn_next_informal_parcel.setEnabled(enable) self.btn_previous_informal_parcel.setEnabled(enable) def _update_informal_labels(self, parcel_number='', current=0, total=0): self.lbl_informal_parcel_number.setText( parcel_number if parcel_number != NULL else 'NULL') out_of = '' if current and total: out_of = QCoreApplication.translate("DockWidgetQueries", "{} out of {}").format( current, total) self.lbl_informal_out_of_total.setText(out_of) def _query_next_informal_parcel(self): parcel_number, current, total = self._controller.get_next_informal_parcel( ) self._search_data_by_component(parcel_number=parcel_number, zoom_and_select=True) self._update_informal_controls(parcel_number, current, total) def _query_previous_informal_parcel(self): parcel_number, current, total = self._controller.get_previous_informal_parcel( ) self._search_data_by_component(parcel_number=parcel_number, zoom_and_select=True) self._update_informal_controls(parcel_number, current, total) def closeEvent(self, event): try: self.canvas.mapToolSet.disconnect(self._initialize_tools) except TypeError as e: pass self.canvas.setMapTool(self.active_map_tool_before_custom)
class DockWidgetQueries(QgsDockWidget, DOCKWIDGET_UI): def __init__(self, iface, db, qgis_utils, ladm_data, parent=None): super(DockWidgetQueries, self).__init__(None) self.setupUi(self) self.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) self.iface = iface self.canvas = iface.mapCanvas() self._db = db self.qgis_utils = qgis_utils self.ladm_data = ladm_data self.selection_color = None self.active_map_tool_before_custom = None self.clipboard = QApplication.clipboard() # Required layers self._plot_layer = None self._parcel_layer = None self._uebaunit_table = None self._identify_tool = None self.add_layers() self.fill_combos() self.btn_identify_plot.setIcon( QIcon(":/Asistente-LADM_COL/resources/images/spatial_unit.png")) # Set connections self.btn_alphanumeric_query.clicked.connect(self.alphanumeric_query) self.btn_clear_alphanumeric_query.clicked.connect( self.clear_alphanumeric_query) self.btn_identify_plot.clicked.connect(self.btn_plot_toggled) # Context menu self._set_context_menus() # Create maptool self.maptool_identify = QgsMapToolIdentifyFeature(self.canvas) def _set_context_menus(self): self.tree_view_basic.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_basic.customContextMenuRequested.connect( self.show_context_menu) self.tree_view_legal.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_legal.customContextMenuRequested.connect( self.show_context_menu) self.tree_view_property_record_card.setContextMenuPolicy( Qt.CustomContextMenu) self.tree_view_property_record_card.customContextMenuRequested.connect( self.show_context_menu) self.tree_view_physical.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_physical.customContextMenuRequested.connect( self.show_context_menu) self.tree_view_economic.setContextMenuPolicy(Qt.CustomContextMenu) self.tree_view_economic.customContextMenuRequested.connect( self.show_context_menu) def add_layers(self): res_layers = self.qgis_utils.get_layers(self._db, { PLOT_TABLE: { 'name': PLOT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry }, PARCEL_TABLE: { 'name': PARCEL_TABLE, 'geometry': None }, UEBAUNIT_TABLE: { 'name': UEBAUNIT_TABLE, 'geometry': None } }, load=True) self._plot_layer = res_layers[PLOT_TABLE] if self._plot_layer is None: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate( "DockWidgetQueries", "Plot layer couldn't be found... {}").format( self._db.get_description()), Qgis.Warning) else: # Layer was found, listen to its removal so that we can deactivate the custom tool when that occurs try: self._plot_layer.willBeDeleted.disconnect(self.layer_removed) except TypeError as e: pass self._plot_layer.willBeDeleted.connect(self.layer_removed) self._parcel_layer = res_layers[PARCEL_TABLE] if self._parcel_layer is None: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate( "DockWidgetQueries", "Parcel layer couldn't be found... {}").format( self._db.get_description()), Qgis.Warning) else: # Layer was found, listen to its removal so that we can update the variable properly try: self._parcel_layer.willBeDeleted.disconnect( self.parcel_layer_removed) except TypeError as e: pass self._parcel_layer.willBeDeleted.connect(self.parcel_layer_removed) self._uebaunit_table = res_layers[UEBAUNIT_TABLE] if self._uebaunit_table is None: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate( "DockWidgetQueries", "UEBAUnit table couldn't be found... {}").format( self._db.get_description()), Qgis.Warning) else: # Layer was found, listen to its removal so that we can update the variable properly try: self._uebaunit_table.willBeDeleted.disconnect( self.uebaunit_table_removed) except TypeError as e: pass self._uebaunit_table.willBeDeleted.connect( self.uebaunit_table_removed) def initialize_tool(self): self._plot_layer = None self.initialize_tools(new_tool=None, old_tool=self.maptool_identify) self.btn_plot_toggled() def update_db_connection(self, db, ladm_col_db): self._db = db self.initialize_tool() if not ladm_col_db: self.setVisible(False) def layer_removed(self): # The required layer was removed, deactivate custom tool self.initialize_tool() def parcel_layer_removed(self): self._parcel_layer = None def uebaunit_table_removed(self): self._uebaunit_table = None def fill_combos(self): self.cbo_parcel_fields.clear() if self._parcel_layer is not None: self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Parcel Number"), 'parcel_number') self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Previous Parcel Number"), 'previous_parcel_number') self.cbo_parcel_fields.addItem( QCoreApplication.translate("DockWidgetQueries", "Folio de Matrícula Inmobiliaria"), 'fmi') else: self.add_layers() def initialize_tools(self, new_tool, old_tool): if self.maptool_identify == old_tool: # custom identify was deactivated try: self.canvas.mapToolSet.disconnect(self.initialize_tools) except TypeError as e: pass if self.selection_color is not None: self.canvas.setSelectionColor( self.selection_color ) # Original selection color set in QGIS self.btn_identify_plot.setChecked(False) else: # custom identify was activated pass def btn_plot_toggled(self): if self.btn_identify_plot.isChecked(): self.prepare_identify_plot() else: # The button was toggled and deactivated, go back to the previous tool self.canvas.setMapTool(self.active_map_tool_before_custom) def prepare_identify_plot(self): """ Custom Identify tool was activated, prepare everything for identifying plots """ self.active_map_tool_before_custom = self.canvas.mapTool() self.selection_color = self.canvas.selectionColor( ) # Probably QColor('#ffff00') self.btn_identify_plot.setChecked(True) self.canvas.mapToolSet.connect(self.initialize_tools) self.canvas.setSelectionColor(QColor("red")) if self._plot_layer is None: self.add_layers() self.maptool_identify.setLayer(self._plot_layer) cursor = QCursor() cursor.setShape(Qt.PointingHandCursor) self.maptool_identify.setCursor(cursor) self.canvas.setMapTool(self.maptool_identify) try: self.maptool_identify.featureIdentified.disconnect() except TypeError as e: pass self.maptool_identify.featureIdentified.connect(self.get_info_by_plot) def get_info_by_plot(self, plot_feature): plot_t_id = plot_feature[ID_FIELD] self.canvas.flashFeatureIds(self._plot_layer, [plot_feature.id()], QColor(255, 0, 0, 255), QColor(255, 0, 0, 0), flashes=1, duration=500) with OverrideCursor(Qt.WaitCursor): self._plot_layer.selectByIds([plot_feature.id()]) if not self.isVisible(): self.show() self.search_data_by_component(plot_t_id=plot_t_id) def search_data_by_component(self, **kwargs): records = self._db.get_igac_basic_info(**kwargs) self.tree_view_basic.setModel(TreeModel(data=records)) self.tree_view_basic.expandAll() records = self._db.get_igac_legal_info(**kwargs) self.tree_view_legal.setModel(TreeModel(data=records)) self.tree_view_legal.expandAll() records = self._db.get_igac_property_record_card_info(**kwargs) self.tree_view_property_record_card.setModel(TreeModel(data=records)) self.tree_view_property_record_card.expandAll() records = self._db.get_igac_physical_info(**kwargs) self.tree_view_physical.setModel(TreeModel(data=records)) self.tree_view_physical.expandAll() records = self._db.get_igac_economic_info(**kwargs) self.tree_view_economic.setModel(TreeModel(data=records)) self.tree_view_economic.expandAll() def alphanumeric_query(self): """ Alphanumeric query """ option = self.cbo_parcel_fields.currentData() query = self.txt_alphanumeric_query.text().strip() if query: if option == 'fmi': self.search_data_by_component(parcel_fmi=query) elif option == 'parcel_number': self.search_data_by_component(parcel_number=query) else: # previous_parcel_number self.search_data_by_component(previous_parcel_number=query) else: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate("DockWidgetQueries", "First enter a query")) def clear_alphanumeric_query(self): self.txt_alphanumeric_query.setText('') def show_context_menu(self, point): tree_view = self.sender() index = tree_view.indexAt(point) context_menu = QMenu("Context menu") index_data = index.data(Qt.UserRole) if index_data is None: return if "value" in index_data: action_copy = QAction( QCoreApplication.translate("DockWidgetQueries", "Copy value")) action_copy.triggered.connect( partial(self.copy_value, index_data["value"])) context_menu.addAction(action_copy) context_menu.addSeparator() # Configure actions for tables/layers if "type" in index_data and "id" in index_data: table_name = index_data["type"] t_id = index_data["id"] geometry_type = None if table_name in DICT_TABLE_PACKAGE and DICT_TABLE_PACKAGE[ table_name] == SPATIAL_UNIT_PACKAGE: # Layers in Spatial Unit package have double geometry, we need the polygon one geometry_type = QgsWkbTypes.PolygonGeometry if table_name == PARCEL_TABLE: if self._parcel_layer is None or self._plot_layer is None or self._uebaunit_table is None: self.add_layers() layer = self._parcel_layer self.iface.layerTreeView().setCurrentLayer(layer) else: layer = self.qgis_utils.get_layer(self._db, table_name, geometry_type, True) if layer is not None: if layer.isSpatial(): action_zoom_to_feature = QAction( QCoreApplication.translate( "DockWidgetQueries", "Zoom to {} with {}={}").format( table_name, ID_FIELD, t_id)) action_zoom_to_feature.triggered.connect( partial(self.zoom_to_feature, layer, t_id)) context_menu.addAction(action_zoom_to_feature) if table_name == PARCEL_TABLE: # We show a handy option to zoom to related plots plot_ids = self.ladm_data.get_plots_related_to_parcel( self._db, t_id, None, self._plot_layer, self._uebaunit_table) if plot_ids: action_zoom_to_plots = QAction( QCoreApplication.translate( "DockWidgetQueries", "Zoom to related plot(s)")) action_zoom_to_plots.triggered.connect( partial(self.zoom_to_plots, plot_ids)) context_menu.addAction(action_zoom_to_plots) action_open_feature_form = QAction( QCoreApplication.translate( "DockWidgetQueries", "Open form for {} with {}={}").format( table_name, ID_FIELD, t_id)) action_open_feature_form.triggered.connect( partial(self.open_feature_form, layer, t_id)) context_menu.addAction(action_open_feature_form) if context_menu.actions(): context_menu.exec_(tree_view.mapToGlobal(point)) def copy_value(self, value): self.clipboard.setText(str(value)) def zoom_to_feature(self, layer, t_id): feature = self.get_feature_from_t_id(layer, t_id) self.iface.mapCanvas().zoomToFeatureIds(layer, [feature.id()]) self.canvas.flashFeatureIds(layer, [feature.id()], QColor(255, 0, 0, 255), QColor(255, 0, 0, 0), flashes=1, duration=500) def open_feature_form(self, layer, t_id): feature = self.get_feature_from_t_id(layer, t_id) self.iface.openFeatureForm(layer, feature) def get_feature_from_t_id(self, layer, t_id): field_idx = layer.fields().indexFromName(ID_FIELD) request = QgsFeatureRequest( QgsExpression("{}={}".format( ID_FIELD, t_id))).setSubsetOfAttributes([field_idx]) request.setFlags(QgsFeatureRequest.NoGeometry) iterator = layer.getFeatures(request) feature = QgsFeature() res = iterator.nextFeature(feature) if res: return feature return None def zoom_to_plots(self, plot_ids): self.iface.mapCanvas().zoomToFeatureIds(self._plot_layer, plot_ids) self.canvas.flashFeatureIds(self._plot_layer, plot_ids, QColor(255, 0, 0, 255), QColor(255, 0, 0, 0), flashes=1, duration=500)
class FeatureSelectorWidget(QWidget): featureIdentified = pyqtSignal(QgsFeature) def __init__(self, parent): QWidget.__init__(self, parent) editLayout = QHBoxLayout() editLayout.setContentsMargins(0, 0, 0, 0) editLayout.setSpacing(2) self.setLayout(editLayout) self.lineEdit = QLineEdit(self) self.lineEdit.setReadOnly(True) editLayout.addWidget(self.lineEdit) self.highlightFeatureButton = QToolButton(self) self.highlightFeatureButton.setPopupMode(QToolButton.MenuButtonPopup) self.highlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionHighlightFeature.svg"), "Highlight feature", self) self.scaleHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionScaleHighlightFeature.svg"), "Scale and highlight feature", self) self.panHighlightFeatureAction = QAction(QgsApplication.getThemeIcon("/mActionPanHighlightFeature.svg"), "Pan and highlight feature", self) self.highlightFeatureButton.addAction(self.highlightFeatureAction) self.highlightFeatureButton.addAction(self.scaleHighlightFeatureAction) self.highlightFeatureButton.addAction(self.panHighlightFeatureAction) self.highlightFeatureButton.setDefaultAction(self.highlightFeatureAction) editLayout.addWidget(self.highlightFeatureButton) self.mapIdentificationButton = QToolButton(self) self.mapIdentificationButton.setIcon(QgsApplication.getThemeIcon("/mActionMapIdentification.svg")) self.mapIdentificationButton.setText("Select on map") self.mapIdentificationButton.setCheckable(True) editLayout.addWidget(self.mapIdentificationButton) self.mapIdentificationButton.clicked.connect(self.mapIdentification) self.highlightFeatureButton.triggered.connect(self.highlightActionTriggered) self.layer = None self.mapTool = None self.canvas = None self.windowWidget = None self.highlight = None self.feature = QgsFeature() def setCanvas(self, mapCanvas): self.mapTool = QgsMapToolIdentifyFeature(mapCanvas) self.mapTool.setButton(self.mapIdentificationButton) self.canvas = mapCanvas def setLayer(self, layer): self.layer = layer def setFeature(self, feature, canvasExtent = CanvasExtent.Fixed): self.lineEdit.clear() self.feature = feature if not self.feature.isValid() or self.layer is None: return featureTitle = feature.attribute(self.layer.displayField()) if featureTitle == '': featureTitle = feature.id() self.lineEdit.setText(str(featureTitle)) self.highlightFeature(canvasExtent) def clear(self): self.feature = QgsFeature() self.lineEdit.clear() def mapIdentification(self): if self.layer is None or self.mapTool is None or self.canvas is None: return self.mapTool.setLayer(self.layer) self.canvas.setMapTool(self.mapTool) self.windowWidget = QWidget.window(self) self.canvas.window().raise_() self.canvas.activateWindow() self.canvas.setFocus() self.mapTool.featureIdentified.connect(self.mapToolFeatureIdentified) self.mapTool.deactivated.connect(self.mapToolDeactivated) def mapToolFeatureIdentified(self, feature): feature = QgsFeature(feature) self.featureIdentified.emit(feature) self.unsetMapTool() self.setFeature(feature) def mapToolDeactivated(self): if self.windowWidget is not None: self.windowWidget.raise_() self.windowWidget.activateWindow() def highlightFeature(self, canvasExtent = CanvasExtent.Fixed): if self.canvas is None or not self.feature.isValid(): return geom = self.feature.geometry() if geom is None: return if canvasExtent == CanvasExtent.Scale: featBBox = geom.boundingBox() featBBox = self.canvas.mapSettings().layerToMapCoordinates(self.layer, featBBox) extent = self.canvas.extent() if not extent.contains(featBBox): extent.combineExtentWith(featBBox) extent.scale(1.1) self.canvas.setExtent(extent) self.canvas.refresh() elif canvasExtent == CanvasExtent.Pan: centroid = geom.centroid() center = centroid.asPoint() center = self.canvas.mapSettings().layerToMapCoordinates(self.layer, center) self.canvas.zoomByFactor(1.0, center) # refresh is done in this method # highlight self.delete_highlight() self.highlight = QgsHighlight(self.canvas, geom, self.layer) settings = QSettings() color = QColor(settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int(settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha())) buffer = 2*float(settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM)) min_width = 2*float(settings.value("/Map/highlight/min_width", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.highlight.setColor(color) # sets also fill with default alpha color.setAlpha(alpha) self.highlight.setFillColor(color) # sets fill with alpha self.highlight.setBuffer(buffer) self.highlight.setMinWidth(min_width) self.highlight.setWidth(4.0) self.highlight.show() self.timer = QTimer(self) self.timer.setSingleShot(True) self.timer.timeout.connect(self.delete_highlight) self.timer.start(3000) def delete_highlight(self): if self.highlight is not None: self.highlight.hide() del self.highlight self.highlight = None def unsetMapTool(self): if self.canvas is not None and self.mapTool is not None: # this will call mapToolDeactivated self.canvas.unsetMapTool(self.mapTool) def highlightActionTriggered(self, action): self.highlightFeatureButton.setDefaultAction(action) if action == self.highlightFeatureAction: self.highlightFeature() elif action == self.scaleHighlightFeatureAction: self.highlightFeature(CanvasExtent.Scale) elif action == self.panHighlightFeatureAction: self.highlightFeature(CanvasExtent.Pan)
class LinkerDock(QDockWidget, Ui_linker, SettingDialog): def __init__(self, iface): # QGIS self.iface = iface self.settings = MySettings() self.linkRubber = QgsRubberBand(self.iface.mapCanvas()) self.featureHighlight = None # Relation management self.relationManager = QgsProject.instance().relationManager() self.relationManager.changed.connect(self.loadRelations) self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationWidgetWrapper = None self.editorContext = QgsAttributeEditorContext() self.editorContext.setVectorLayerTools(self.iface.vectorLayerTools()) # GUI QDockWidget.__init__(self) self.setupUi(self) SettingDialog.__init__(self, MySettings(), False, True) self.drawButton.setChecked(self.settings.value("drawEnabled")) self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) self.mapTool = QgsMapToolIdentifyFeature(self.iface.mapCanvas()) self.mapTool.setButton(self.identifyReferencingFeatureButton) # Connect signal/slot self.relationComboBox.currentIndexChanged.connect( self.currentRelationChanged) self.mapTool.featureIdentified.connect(self.setReferencingFeature) # load relations at start self.loadRelations() def showEvent(self, QShowEvent): self.drawLink() def closeEvent(self, e): self.iface.mapCanvas().unsetMapTool(self.mapTool) self.linkRubber.reset() self.deleteHighlight() self.deleteWrapper() self.disconnectLayer() def disconnectLayer(self): if self.relation.isValid(): self.relation.referencingLayer().editingStarted.disconnect( self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect( self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.disconnect( self.layerValueChangedOutside) def runForFeature(self, relationId, layer, feature): index = self.relationComboBox.findData(relationId) self.relationComboBox.setCurrentIndex(index) self.setReferencingFeature(feature) self.show() if not layer.isEditable(): self.iface.messageBar().pushMessage( "Link It", "Cannot set a new related feature since %s is not editable" % layer.name(), QgsMessageBar.WARNING, 4) else: self.relationReferenceWidget.mapIdentification() @pyqtSlot(name="on_identifyReferencingFeatureButton_clicked") def activateMapTool(self): self.iface.mapCanvas().setMapTool(self.mapTool) def deactivateMapTool(self): self.iface.mapCanvas().unsetMapTool(self.mapTool) def loadRelations(self): self.deleteWrapper() self.disconnectLayer() self.relation = QgsRelation() self.referencingFeature = QgsFeature() self.relationComboBox.currentIndexChanged.disconnect( self.currentRelationChanged) self.relationComboBox.clear() for relation in self.relationManager.referencedRelations(): if relation.referencingLayer().hasGeometryType(): self.relationComboBox.addItem(relation.name(), relation.id()) self.relationComboBox.setCurrentIndex(-1) self.relationComboBox.currentIndexChanged.connect( self.currentRelationChanged) self.currentRelationChanged(-1) def currentRelationChanged(self, index): # disconnect previous relation if self.relation.isValid(): try: self.relation.referencingLayer().editingStarted.disconnect( self.relationEditableChanged) self.relation.referencingLayer().editingStopped.disconnect( self.relationEditableChanged) self.relation.referencingLayer( ).attributeValueChanged.disconnect( self.layerValueChangedOutside) except TypeError: pass self.referencingFeatureLayout.setEnabled(index >= 0) relationId = self.relationComboBox.itemData(index) self.relation = self.relationManager.relation(relationId) self.mapTool.setLayer(self.relation.referencingLayer()) self.setReferencingFeature() # connect if self.relation.isValid(): self.relation.referencingLayer().editingStarted.connect( self.relationEditableChanged) self.relation.referencingLayer().editingStopped.connect( self.relationEditableChanged) self.relation.referencingLayer().attributeValueChanged.connect( self.layerValueChangedOutside) def setReferencingFeature(self, feature=QgsFeature()): self.deactivateMapTool() self.referencingFeature = QgsFeature(feature) self.deleteWrapper() # disable relation reference widget if no referencing feature self.referencedFeatureLayout.setEnabled(feature.isValid()) # set line edit if not self.relation.isValid() or not feature.isValid(): self.referencingFeatureLineEdit.clear() return self.referencingFeatureLineEdit.setText("%s" % feature.id()) fieldIdx = self.referencingFieldIndex() widgetConfig = self.relation.referencingLayer().editorWidgetV2Config( fieldIdx) self.relationWidgetWrapper = QgsEditorWidgetRegistry.instance().create( "RelationReference", self.relation.referencingLayer(), fieldIdx, widgetConfig, self.relationReferenceWidget, self, self.editorContext) self.relationWidgetWrapper.setEnabled( self.relation.referencingLayer().isEditable()) self.relationWidgetWrapper.setValue(feature[fieldIdx]) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) # override field definition to allow map identification self.relationReferenceWidget.setAllowMapIdentification(True) self.relationReferenceWidget.setEmbedForm(False) # update drawn link self.highlightReferencingFeature() self.drawLink() def deleteWrapper(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.valueChanged.disconnect( self.foreignKeyChanged) self.relationWidgetWrapper.setValue(None) del self.relationWidgetWrapper self.relationWidgetWrapper = None def foreignKeyChanged(self, newKey): if not self.relation.isValid() or not self.relation.referencingLayer( ).isEditable() or not self.referencingFeature.isValid(): self.drawLink() return if not self.relation.referencingLayer().editBuffer( ).changeAttributeValue(self.referencingFeature.id(), self.referencingFieldIndex(), newKey): self.iface.messageBar().pushMessage( "Link It", "Cannot change attribute value.", QgsMessageBar.CRITICAL) self.drawLink() def relationEditableChanged(self): if self.relationWidgetWrapper is not None: self.relationWidgetWrapper.setEnabled( self.relation.isValid() and self.relation.referencingLayer().isEditable()) def layerValueChangedOutside(self, fid, fieldIdx, value): if not self.relation.isValid() or not self.referencingFeature.isValid( ) or self.relationWidgetWrapper is None: return # not the correct feature if fid != self.referencingFeature.id(): return # not the correct field if fieldIdx != self.referencingFieldIndex(): return # widget already has this value if value == self.relationWidgetWrapper.value(): return self.relationWidgetWrapper.valueChanged.disconnect( self.foreignKeyChanged) self.relationWidgetWrapper.setValue(value) self.relationWidgetWrapper.valueChanged.connect(self.foreignKeyChanged) def referencingFieldIndex(self): if not self.relation.isValid(): return -1 fieldName = self.relation.fieldPairs().keys()[0] fieldIdx = self.relation.referencingLayer().fieldNameIndex(fieldName) return fieldIdx @pyqtSlot(bool, name="on_drawButton_toggled") def drawLink(self): self.settings.setValue("drawEnabled", self.drawButton.isChecked()) self.linkRubber.reset() if not self.drawButton.isChecked( ) or not self.referencingFeature.isValid( ) or not self.relation.isValid(): return referencedFeature = self.relationReferenceWidget.referencedFeature() if not referencedFeature.isValid(): return p1 = self.centroid(self.relation.referencedLayer(), referencedFeature) p2 = self.centroid(self.relation.referencingLayer(), self.referencingFeature) geom = arc(p1, p2) self.linkRubber.setToGeometry(geom, None) self.linkRubber.setWidth(self.settings.value("rubberWidth")) self.linkRubber.setColor(self.settings.value("rubberColor")) self.linkRubber.setLineStyle(Qt.DashLine) def centroid(self, layer, feature): geom = feature.geometry() if geom.type() == QGis.Line: geom = geom.interpolate(geom.length() / 2) else: geom = geom.centroid() return self.iface.mapCanvas().mapSettings().layerToMapCoordinates( layer, geom.asPoint()) @pyqtSlot(name="on_highlightReferencingFeatureButton_clicked") def highlightReferencingFeature(self): self.deleteHighlight() if not self.relation.isValid() or not self.referencingFeature.isValid( ): return self.featureHighlight = QgsHighlight( self.iface.mapCanvas(), self.referencingFeature.geometry(), self.relation.referencingLayer()) settings = QSettings() color = QColor( settings.value("/Map/highlight/color", QGis.DEFAULT_HIGHLIGHT_COLOR.name())) alpha = int( settings.value("/Map/highlight/colorAlpha", QGis.DEFAULT_HIGHLIGHT_COLOR.alpha())) bbuffer = float( settings.value("/Map/highlight/buffer", QGis.DEFAULT_HIGHLIGHT_BUFFER_MM)) minWidth = float( settings.value("/Map/highlight/minWidth", QGis.DEFAULT_HIGHLIGHT_MIN_WIDTH_MM)) self.featureHighlight.setColor(color) color.setAlpha(alpha) self.featureHighlight.setFillColor(color) self.featureHighlight.setBuffer(bbuffer) self.featureHighlight.setMinWidth(minWidth) self.featureHighlight.show() timer = QTimer(self) timer.setSingleShot(True) timer.timeout.connect(self.deleteHighlight) timer.start(3000) def deleteHighlight(self): if self.featureHighlight: del self.featureHighlight self.featureHighlight = None
class AssociateExtAddressWizard(QWizard, WIZARD_UI): def __init__(self, iface, db, qgis_utils, parent=None): QWizard.__init__(self, parent) self.setupUi(self) self.iface = iface self.log = QgsApplication.messageLog() self._db = db self.qgis_utils = qgis_utils self.canvas = self.iface.mapCanvas() self.maptool = self.canvas.mapTool() self.maptool_identify = None self.help_strings = HelpStrings() self.translatable_config_strings = TranslatableConfigStrings() self._extaddress_layer = None self._plot_layer = None self._building_layer = None self._building_unit_layer = None self._current_layer = None self._feature_tid = None self.restore_settings() self.rad_to_plot.toggled.connect(self.adjust_page_1_controls) self.rad_to_building.toggled.connect(self.adjust_page_1_controls) self.rad_to_building_unit.toggled.connect(self.adjust_page_1_controls) self.adjust_page_1_controls() self.button(QWizard.NextButton).clicked.connect(self.prepare_selection) self.button(QWizard.FinishButton).clicked.connect(self.finished_dialog) self.button(QWizard.HelpButton).clicked.connect(self.show_help) self.mMapLayerComboBox.setFilters(QgsMapLayerProxyModel.PolygonLayer) self.bar = QgsMessageBar() self.bar.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.setLayout(QGridLayout()) self.layout().addWidget(self.bar, 0, 0, Qt.AlignTop) def adjust_page_1_controls(self): self.cbo_mapping.clear() self.cbo_mapping.addItem("") self.cbo_mapping.addItems( self.qgis_utils.get_field_mappings_file_names(EXTADDRESS_TABLE)) if self.rad_refactor.isChecked(): self.lbl_refactor_source.setEnabled(True) self.mMapLayerComboBox.setEnabled(True) self.lbl_field_mapping.setEnabled(True) self.cbo_mapping.setEnabled(True) disable_next_wizard(self) FinishButton_text = QCoreApplication.translate( "AssociateExtAddressWizard", "Import") self.txt_help_page_1.setHtml( self.help_strings.get_refactor_help_string( EXTADDRESS_TABLE, True)) self.wizardPage1.setFinalPage(True) self.wizardPage1.setButtonText( QWizard.FinishButton, QCoreApplication.translate("AssociateExtAddressWizard", FinishButton_text)) elif self.rad_to_plot.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate( "AssociateExtAddressWizard", "Associate Plot ExtAddress") self.txt_help_page_1.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_1) elif self.rad_to_building.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate( "AssociateExtAddressWizard", "Associate Building ExtAddress") self.txt_help_page_1.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_2) else: #self.rad_to_building_unit.isChecked(): self.lbl_refactor_source.setEnabled(False) self.mMapLayerComboBox.setEnabled(False) self.lbl_field_mapping.setEnabled(False) self.cbo_mapping.setEnabled(False) self.wizardPage1.setFinalPage(False) enable_next_wizard(self) FinishButton_text = QCoreApplication.translate( "AssociateExtAddressWizard", "Associate Building Unit ExtAddress") self.txt_help_page_1.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_1_OPTION_3) self.wizardPage2.setButtonText( QWizard.FinishButton, QCoreApplication.translate('AssociateExtAddressWizard', FinishButton_text)) def prepare_selection(self): self.button(self.FinishButton).setDisabled(True) if self.rad_to_plot.isChecked(): self.btn_select.setText( QCoreApplication.translate("AssociateExtAddressWizard", "Select Plot")) self.txt_help_page_2.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_1) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: { 'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry }, PLOT_TABLE: { 'name': PLOT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry } }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._plot_layer = res_layers[PLOT_TABLE] self._current_layer = self._plot_layer elif self.rad_to_building.isChecked(): self.btn_select.setText( QCoreApplication.translate("AssociateExtAddressWizard", "Select Building")) self.txt_help_page_2.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_2) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: { 'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry }, BUILDING_TABLE: { 'name': BUILDING_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry } }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._building_layer = res_layers[BUILDING_TABLE] self._current_layer = self._building_layer else: #self.rad_to_building_unit.isChecked(): self.btn_select.setText( QCoreApplication.translate("AssociateExtAddressWizard", "Select Building Unit")) self.txt_help_page_2.setHtml( self.help_strings. WIZ_ASSOCIATE_EXTADDRESS_CADASTRE_PAGE_2_OPTION_3) # Load layers res_layers = self.qgis_utils.get_layers(self._db, { EXTADDRESS_TABLE: { 'name': EXTADDRESS_TABLE, 'geometry': QgsWkbTypes.PointGeometry }, BUILDING_UNIT_TABLE: { 'name': BUILDING_UNIT_TABLE, 'geometry': QgsWkbTypes.PolygonGeometry } }, load=True) self._extaddress_layer = res_layers[EXTADDRESS_TABLE] self._building_unit_layer = res_layers[BUILDING_UNIT_TABLE] self._current_layer = self._building_unit_layer self.iface.setActiveLayer(self._current_layer) self.check_selected_features() self.btn_select.clicked.connect(self.select_feature) self.btn_select_by_expression.clicked.connect( self.select_feature_by_expression) def check_selected_features(self): self.bar.clearWidgets() if self._current_layer.selectedFeatureCount() == 1: self.lbl_selected.setText( QCoreApplication.translate("AssociateExtAddressWizard", "1 Feature Selected")) self.button(self.FinishButton).setDisabled(False) self._feature_tid = self._current_layer.selectedFeatures( )[0][ID_FIELD] self.canvas.zoomToSelected(self._current_layer) elif self._current_layer.selectedFeatureCount() > 1: self.show_message( QCoreApplication.translate("AssociateExtAddressWizard", "Please select just one feature"), Qgis.Warning) self.lbl_selected.setText( QCoreApplication.translate( "AssociateExtAddressWizard", "{} Feature(s) Selected".format( self._current_layer.selectedFeatureCount()))) self.button(self.FinishButton).setDisabled(True) else: self.lbl_selected.setText( QCoreApplication.translate("AssociateExtAddressWizard", "0 Features Selected")) self.button(self.FinishButton).setDisabled(True) def select_feature_by_expression(self): Dlg_expression_selection = QgsExpressionSelectionDialog( self._current_layer) self._current_layer.selectionChanged.connect( self.check_selected_features) Dlg_expression_selection.exec() self._current_layer.selectionChanged.disconnect( self.check_selected_features) def select_feature(self): self.setVisible(False) # Make wizard disappear # Create maptool self.maptool_identify = QgsMapToolIdentifyFeature(self.canvas) self.maptool_identify.setLayer(self._current_layer) cursor = QCursor() cursor.setShape(Qt.CrossCursor) self.maptool_identify.setCursor(cursor) self.canvas.setMapTool(self.maptool_identify) self.maptool_identify.featureIdentified.connect(self.get_feature_id) # TODO: Take into account that a user can select another tool def get_feature_id(self, feature): self.setVisible(True) # Make wizard appear if feature: self.lbl_selected.setText( QCoreApplication.translate("AssociateExtAddressWizard", "1 Feature Selected")) self._current_layer.selectByIds([feature.id()]) self.canvas.setMapTool(self.maptool) self.check_selected_features() self.maptool_identify.featureIdentified.disconnect(self.get_feature_id) self.log.logMessage( "Spatial Unit's featureIdentified SIGNAL disconnected", PLUGIN_NAME, Qgis.Info) def finished_dialog(self): self.save_settings() if self.rad_refactor.isChecked(): if self.mMapLayerComboBox.currentLayer() is not None: field_mapping = self.cbo_mapping.currentText() res_etl_model = self.qgis_utils.show_etl_model( self._db, self.mMapLayerComboBox.currentLayer(), EXTADDRESS_TABLE, field_mapping=field_mapping) if res_etl_model: if field_mapping: self.qgis_utils.delete_old_field_mapping(field_mapping) self.qgis_utils.save_field_mapping(EXTADDRESS_TABLE) else: self.iface.messageBar().pushMessage( 'Asistente LADM_COL', QCoreApplication.translate( "AssociateExtAddressWizard", "Select a source layer to set the field mapping to '{}'." ).format(EXTADDRESS_TABLE), Qgis.Warning) else: self.prepare_extaddress_creation() def prepare_extaddress_creation(self): # Don't suppress (i.e., show) feature form form_config = self._extaddress_layer.editFormConfig() form_config.setSuppress(QgsEditFormConfig.SuppressOff) self._extaddress_layer.setEditFormConfig(form_config) self.edit_extaddress() def edit_extaddress(self): if self._current_layer.selectedFeatureCount() == 1: # Open Form self.iface.layerTreeView().setCurrentLayer(self._extaddress_layer) self._extaddress_layer.startEditing() self.iface.actionAddFeature().trigger() # Create connections to react when a feature is added to buffer and # when it gets stored into the DB self._extaddress_layer.featureAdded.connect( self.call_extaddress_commit) else: self.iface.messageBar().pushMessage( "Asistente LADM_COL", QCoreApplication.translate("AssociateExtAddressWizard", "Please select a feature"), Qgis.Warning) def call_extaddress_commit(self, fid): plot_field_idx = self._extaddress_layer.getFeature(fid).fieldNameIndex( EXTADDRESS_PLOT_FIELD) building_field_idx = self._extaddress_layer.getFeature( fid).fieldNameIndex(EXTADDRESS_BUILDING_FIELD) building_unit_field_idx = self._extaddress_layer.getFeature( fid).fieldNameIndex(EXTADDRESS_BUILDING_UNIT_FIELD) if self._current_layer.name() == PLOT_TABLE: self._extaddress_layer.changeAttributeValue( fid, plot_field_idx, self._feature_tid) elif self._current_layer.name() == BUILDING_TABLE: self._extaddress_layer.changeAttributeValue( fid, building_field_idx, self._feature_tid) else: #self._current_layer.name() == BUILDING_UNIT_TABLE: self._extaddress_layer.changeAttributeValue( fid, building_unit_field_idx, self._feature_tid) self._extaddress_layer.featureAdded.disconnect( self.call_extaddress_commit) self.log.logMessage("Extaddres's featureAdded SIGNAL disconnected", PLUGIN_NAME, Qgis.Info) res = self._extaddress_layer.commitChanges() self._current_layer.removeSelection() def show_message(self, message, level): self.bar.pushMessage(message, level, 0) def save_settings(self): settings = QSettings() load_data_type = 'refactor' if self.rad_to_plot.isChecked(): load_data_type = 'to_plot' elif self.rad_to_building.isChecked(): load_data_type = 'to_building' else: #self.rad_to_building_unit.isChecked(): load_data_type = 'to_building_unit' settings.setValue( 'Asistente-LADM_COL/wizards/ext_address_load_data_type', load_data_type) def restore_settings(self): settings = QSettings() load_data_type = settings.value( 'Asistente-LADM_COL/wizards/ext_address_load_data_type', 'to_plot') if load_data_type == 'refactor': self.rad_refactor.setChecked(True) elif load_data_type == 'to_plot': self.rad_to_plot.setChecked(True) elif load_data_type == 'to_building': self.rad_to_building.setChecked(True) else: #load_data_type == 'to_building_unit': self.rad_to_building_unit.setChecked(True) def show_help(self): self.qgis_utils.show_help("associate_ext_address")