[GIS] How to display data from a feature

developmentpyqgisqgisqgis-pluginsreferences

I want to display data based on the feature currently selected/clicked on.

I am following a tutorial from http://www.qgisworkshop.org/html/workshop/plugins_tutorial.html.

It initially shows you how to use the Textbrowser to display the coordinates currently clicked on and developing that functionality further, it allows you to see the information regarding a respective feature.

I have the following function which as per example in the above mentioned tutorial is set to retrieve the respective data. In the example it refers to the field name "NAME" which from what I can understand refers to the name of the field in the attribute table for the respective layer in question.

My attribute table structure is as follows:

field names:

objectid
str_add_id
building_number
building_name_id
street_name_id
etc etc

The field I wish to retrieve is that of the "str_add_id" therefore using his example in the tutorial:

His Example:

def updateTextBrowser(self):
    # if we have a selected feature
    if self.selectList:
        # find the index of the 'NAME' column, branch if has one or not
        nIndx = self.provider.fieldNameIndex('NAME')
        # get our selected feature from the provider, but we have to pass in an empty feature and the column index we want
        sFeat = QgsFeature()
        if self.provider.featureAtId(self.selectList[0], sFeat, True, [nIndx]):
            # only if we have a 'NAME' column
            if nIndx != -1:
                # get the feature attributeMap
                attMap = sFeat.attributeMap()
                # clear old TextBrowser values
                self.dlg.clearTextBrowser()
                # now update the TextBrowser with attributeMap[nameColumnIndex]
                # when we first retrieve the value of 'NAME' it comes as a QString so we have to cast it to a Python string
                self.dlg.setTextBrowser( str( attMap[nIndx].toString() ))

In my example, I basically replaced the nIndx = self.provider.fieldNameIndex('NAME') changing the "'NAME'" to "'str_add_id'".

Unfortunately however upon clicking on the feature I do not get the requested data. I still receive the X and Y coordinates of the point clicked on in the layer.

Any ideas as to what I could be doing wrong? I have attached my whole plugin code below:

# -*- coding: utf-8 -*-
"""
/***************************************************************************
 testplug
                                 A QGIS plugin
 Test plugin through builder
                              -------------------
        begin                : 2013-05-08
        copyright            : (C) 2013 by Test
        email                : cedric.ramirez@gibraltar.gov.gi
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
# Import the PyQt and QGIS libraries
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import *
# Initialize Qt resources from file resources.py
import resources
# Import the code for the dialog
from testplugdialog import testplugDialog


class testplug:

    def __init__(self, iface):
        # Save reference to the QGIS interface
        self.iface = iface
                #A reference to our map canvas
        self.canvas = self.iface.mapCanvas()
                #Change
                # this QGIS tool emits as QgsPoint after each click on the map canvas
        self.clickTool = QgsMapToolEmitPoint(self.canvas)

        # create our GUI dialog
        self.dlg = testplugDialog()

        # create a list to hold our selected feature ids
        self.selectList = []
        # current layer ref (set in handleLayerChange)
        self.cLayer = None
        # current layer dataProvider ref (set in handleLayerChange)
        self.provider = None

        # initialize plugin directory
        self.plugin_dir = QFileInfo(QgsApplication.qgisUserDbFilePath()).path() + "/python/plugins/testplug"
        # initialize locale
        localePath = ""
        locale = QSettings().value("locale/userLocale").toString()[0:2]

        if QFileInfo(self.plugin_dir).exists():
            localePath = self.plugin_dir + "/i18n/testplug_" + locale + ".qm"

        if QFileInfo(localePath).exists():
            self.translator = QTranslator()
            self.translator.load(localePath)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = testplugDialog()

    def initGui(self):
        # Create action that will start plugin configuration
        self.action = QAction(
            QIcon(":/plugins/testplug/icon.png"),
            u"Test Plugin through builder", self.iface.mainWindow())
        # connect the action to the run method
        QObject.connect(self.action, SIGNAL("triggered()"), self.run)

        # Add toolbar button and menu item
        self.iface.addToolBarIcon(self.action)
        self.iface.addPluginToMenu(u"&testplug", self.action)

        # connect our custom function to a clickTool signal that the canvas was clicked
        #result = QObject.connect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.handleMouseDown)
        #QMessageBox.information( self.iface.mainWindow(),"Info", "connect = %s"%str(result) )

        # connect our select function to the canvasClicked signal
        #result = QObject.connect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.selectFeature)
        #QMessageBox.information( self.iface.mainWindow(),"Info", "connect = %s"%str(result) )

        QObject.connect(self.dlg.ui.chkActivate,SIGNAL("stateChanged(int)"),self.changeActive)

        # connect to the currentLayerChanged signal of QgsInterface
        result = QObject.connect(self.iface, SIGNAL("currentLayerChanged(QgsMapLayer *)"), self.handleLayerChange)
        # QMessageBox.information( self.iface.mainWindow(),"Info", "connect = %s"%str(result) )

    def handleLayerChange(self, layer):
        self.cLayer = self.canvas.currentLayer()
        if self.cLayer:
            self.provider = self.cLayer.dataProvider()

    def changeActive(self,state):
        if (state==Qt.Checked):
            # connect to click signal
            QObject.connect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.handleMouseDown)
            # connect our select function to the canvasClicked signal
            QObject.connect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.selectFeature)
        else:
            # disconnect from click signal
            QObject.disconnect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.handleMouseDown)
            # disconnect our select function to the canvasClicked signal
            QObject.disconnect(self.clickTool, SIGNAL("canvasClicked(const QgsPoint &, Qt::MouseButton)"), self.selectFeature)




    def unload(self):
        # Remove the plugin menu item and icon
        self.iface.removePluginMenu(u"&testplug", self.action)
        self.iface.removeToolBarIcon(self.action)

    def handleMouseDown(self, point, button):
        self.dlg.clearTextBrowser()
        self.dlg.setTextBrowser( str(point.x()) + " , " +str(point.y()) )
        #QMessageBox.information( self.iface.mainWindow(),"Info", "X,Y = %s,%s" % (str(point.x()),str(point.y())) )


    def selectFeature(self, point, button):
        # QMessageBox.information( self.iface.mainWindow(),"Info", "in selectFeature function" )
        # setup the provider select to filter results based on a rectangle
        pntGeom = QgsGeometry.fromPoint(point)
        # scale-dependent buffer of 2 pixels-worth of map units
        pntBuff = pntGeom.buffer( (self.canvas.mapUnitsPerPixel() * 2),0)
        rect = pntBuff.boundingBox()
        # get currentLayer and dataProvider
        self.cLayer = self.canvas.currentLayer()
        self.selectList = []
        if self.cLayer:
            self.provider = self.cLayer.dataProvider()
            feat = QgsFeature()
            # create the select statement
            self.provider.select([],rect) # the arguments mean no attributes returned, and do a bbox filter with our buffered rectangle to limit the amount of features
            while self.provider.nextFeature(feat):
                # if the feat geom returned from the selection intersects our point then put it in a list
                if feat.geometry().intersects(pntGeom):
                    self.selectList.append(feat.id())

            if self.selectList:
                # make the actual selection
                self.cLayer.setSelectedFeatures(self.selectList)
                # update the TextBrowser
                self.updateTextBrowser()
        else:
            QMessageBox.information( self.iface.mainWindow(),"Info", "No layer currently selected in TOC" )


    def updateTextBrowser(self):
        # if we have a selected feature
        if self.selectList:
            # find the index of the 'NAME' column, branch if has one or not
            nIndx = self.provider.fieldNameIndex('objectid')
            # get our selected feature from the provider, but we have to pass in an empty feature and the column index we want
            sFeat = QgsFeature()
            if self.provider.featureAtId(self.selectList[0], sFeat, True, [nIndx]):
                # only if we have a 'NAME' column
                if nIndx != -1:
                    # get the feature attributeMap
                    attMap = sFeat.attributeMap()
                    # clear old TextBrowser values
                    self.dlg.clearTextBrowser()
                    # now update the TextBrowser with attributeMap[nameColumnIndex]
                    # when we first retrieve the value of 'NAME' it comes as a QString so we have to cast it to a Python string
                    self.dlg.setTextBrowser( str( attMap[nIndx].toString() ))

        # run method that performs all the real work
    def run(self):
        # set the current layer immediately if it exists, otherwise it will be set on user selection
        self.cLayer = self.iface.mapCanvas().currentLayer()
        if self.cLayer: self.provider = self.cLayer.dataProvider()

        # make our clickTool the tool that we'll use for now
        self.canvas.setMapTool(self.clickTool)
        # show the dialog
        self.dlg.show()



        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result == 1:
            # do something useful (delete the line containing pass and
            # substitute with your code)
            pass

Thanks in advance!

Best Answer

You still have handleMouseDown connected to canvasClicked. Both methods ( handleMouseDown and selectFeature ) will be called and as they both clear and write the box, the first one will be overwritten by the second one. Just remove one of the two connects and you should be fine.

As a side-note, if your project gets bigger you would probably want to separate this logic from the main plugin code. QgsMapTool facilitates stuff like this. See also How to programatically check for a mouse click in QGIS

Related Question