Identify feature on map

A very awaited feature is now available in the master version of QGIS: identifying features in the map!

You can define the class of the map tool as follows:

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
from qgis.gui import *

class IdentifyGeometry(QgsMapToolIdentify):
 def __init__(self, canvas):
  self.canvas = canvas
  QgsMapToolIdentify.__init__(self, canvas)

 def canvasReleaseEvent(self, mouseEvent):
  results = self.identify(mouseEvent.x(),mouseEvent.y(), self.TopDownStopAtFirst, self.VectorLayer)
  if len(results) > 0:
   self.emit( SIGNAL( "geomIdentified" ), results[0].mLayer, results[0].mFeature)

This class will try to identify a feature of any visible vector layer and returning the first found feature (using layer order). Then, it will emit the signal with the layer and the feature identified.
To customize this, you can use the identify method with different arguments:

  • type of layer
  • type of identification (current layer, top-down, top-down stop at first or the QGIS setting)
  • list of layers

There is two ways of calling the identify methods:

  • identify (x, y, layerList=[], IdentifyMode mode=self.DefaultQgsSetting)
  • identify (x, y, identifyMode, layerType=AllLayers)

Identify mode and layer types are defined here. Mainly the options can be:

  • Identify mode: self.DefaultQgsSetting, self.ActiveLayer, self.TopDownStopAtFirst, self.TopDownAll
  • Layer type: self.AllLayers, self.VectorLayer, self.RasterLayer

Both methods return a structure IdentifyResult defined in the API. Mainly, it contains:

  • the feature (mFeature) if the identified layer is a vector layer
  • the corresponding layer (mLayer)
  • the derived attributes (mDerivedAttributes): the raster value for raster layers

In your plugin main code, you can define a toolbox button to enable your map tool:

class myPlugin():
 def initGui(self):
  self.mapToolAction = QAction(QIcon(":/plugins/myPlugin/icons/myIcon.png"), "My Plugin", self.iface.mainWindow())
  self.mapToolAction.setCheckable(True)
  QObject.connect(self.mapToolAction, SIGNAL("triggered()"), self.mapToolInit)
  self.iface.addToolBarIcon(self.mapToolAction)
  self.iface.addPluginToMenu("&My Plugin", self.mapToolAction)

 def mapToolInit(self):
  canvas = self.iface.mapCanvas()
  if self.mapToolAction.isChecked() is False:
   canvas.unsetMapTool(self.mapTool)
   return
  self.mapToolAction.setChecked( True )
  self.mapTool = IdentifyGeometry(canvas)
  QObject.connect(self.mapTool , SIGNAL("geomIdentified") , self.doSometing )
  canvas.setMapTool(self.mapTool)
  QObject.connect( canvas, SIGNAL( "mapToolSet(QgsMapTool *)" ), self.mapToolChanged)</em>

 def doSomething(self, layer, feature):
  # do something

If you want your plugin to be back compatible with version before 1.9, you can select the features at the clicked point using a given tolerance and using the current layer:

try:
 from qgis.gui import QgsMapToolIdentify
except:
 from qgis.gui import QgsMapTool as QgsMapToolIdentify

class IdentifyGeometry(QgsMapToolIdentify):
 def __init__(self, canvas):
  self.canvas = canvas
  QgsMapToolIdentify.__init__(self, canvas)

 def canvasReleaseEvent(self, mouseEvent):
  try:
  results = self.identify(mouseEvent.x(),mouseEvent.y(), self.TopDownStopAtFirst, self.VectorLayer)
  if len(results) > 0:
   self.emit( SIGNAL( "geomIdentified" ), results[0].mLayer, results[0].mFeature)
  except: # qgis <1.9
   point = self.toMapCoordinates( mouseEvent.pos() )
   layer = self.canvas.currentLayer()
   if layer == None:
    return
   if layer.type() != QgsMapLayer.VectorLayer:
    return
   point = self.canvas.mapRenderer().mapToLayerCoordinates(layer, point)
   pixTolerance = 6
   mapTolerance = pixTolerance * self.canvas.mapUnitsPerPixel()
   rect = QgsRectangle(point.x()-mapTolerance,point.y()-mapTolerance,point.x()+mapTolerance,point.y()+mapTolerance)
   provider = layer.dataProvider()
   provider.select([], rect, True, True)
   subset = []
   f = QgsFeature()
   while (provider.nextFeature(f)):
    subset.append(f)
    if len(subset) == 0:
     return
    if len(subset) > 1:
     idx = QgsSpatialIndex()
    for f in subset:
     idx.insertFeature(f)
     nearest = idx.nearestNeighbor( point, 1 )
     layer.featureAtId(nearest[0],f, True, False)
    self.emit( SIGNAL( "geomIdentified" ), layer, f)

Note, that this last code (for version <1.9) does not consider scale dependent visibility and can therefore return a feature which is not visible in the map!

Advertisements