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!