属性识别工具,也就是常用的 identify 工具,它常常与诸如放大、缩小等地图工具放在一起,提供浏览地图要素的一项基本功能。为什么要单独讨论一下这个工具,是因为它与普通的地图浏览工具的实现有一些微小的差异。下面通过源代码的学习,来了解这个工具的实现方法以及掌握属性识别功能的实现机制。
![这里写图片描述](https://img-blog.csdn.net/20160204164221721)
相关类
要实现一个功能,首先自然是找到这个功能相关的类,并查看类之间的一些关系。这里,属性识别也是地图工具,因此,先查看一下地图工具类,也就是 QgsMapTool 类。QgsMapTool 类是一个抽象类,它的子类负责地图浏览功能。
![这里写图片描述](https://img-blog.csdn.net/20160204190151852)
通过上图可以看到几个基本的工具类
- 漫游工具 – QgsMapToolPan
- 触摸工具(需要触摸屏的支持) – QgsMapToolTouch
- 缩放工具 – QgsMapToolZoom (放大和缩小仅仅是缩放系数不同来而已,因此缩放共用一个类)
这些工具的使用非常简单,只需要初始化一个对应工具类的实例,并设置地图画布的 MapTool 为相应的实例就可以了,代码如下:
QgsMapToolPan* m_mapToolPan = new QgsMapToolPan( m_mapCanvas );
QgsMapToolZoom* m_mapToolZoomIn = new QgsMapToolZoom( m_mapCanvas, false );
QgsMapToolZoom* m_mapToolZoomOut = new QgsMapToolZoom( m_mapCanvas, true );
m_mapCanvas->setMapTool( m_mapToolPan );
还有一个 QgsMapToolEmitPoint 类,是用来发出地图上用户选点坐标的,这个工具可以做一些自定义的地图交互功能,在图层文件进行编辑的时候也可以使用。
除了上面说的这些,剩下两个类 QgsMapToolIdentify 和它的派生类 QgsMapToolIdentifyFeature。这两个类就是我们关注的重点了,看名字就知道这两个类负责属性的识别了。
这个类的用法与上面提到的工具有点微小的区别,如果像上面那样设置工具,虽然切换成功后鼠标图标会变成识别工具的样式,但是无论如何点击都不会有任何效果。这是因为,在这个类的实现代码中,鼠标事件的实现部分是下面这样的:
void QgsMapToolIdentify::canvasMoveEvent( QMouseEvent * e )
{
Q_UNUSED( e );
}
void QgsMapToolIdentify::canvasPressEvent( QMouseEvent * e )
{
Q_UNUSED( e );
}
void QgsMapToolIdentify::canvasReleaseEvent( QMouseEvent * e )
{
Q_UNUSED( e );
}
并没有实现代码,因此鼠标事件是不被处理的。
再来关注定义在这个类中的一个结构体,它定义了属性识别的返回结果。
struct IdentifyResult
IdentifyResult( QgsMapLayer * layer, QgsFeature feature, QMap< QString, QString > derivedAttributes ):
mLayer( layer ), mFeature( feature ), mDerivedAttributes( derivedAttributes )
IdentifyResult( QgsMapLayer * layer, QString label, QMap< QString, QString > attributes, QMap< QString, QString > derivedAttributes ):
mLayer( layer ), mLabel( label ), mAttributes( attributes ), mDerivedAttributes( derivedAttributes )
IdentifyResult( QgsMapLayer * layer, QString label, QgsFields fields, QgsFeature feature, QMap< QString, QString > derivedAttributes ):
mLayer( layer ), mLabel( label ), mFields( fields ), mFeature( feature ), mDerivedAttributes( derivedAttributes )
QgsMapLayer* mLayer;
QString mLabel;
QgsFields mFields;
QgsFeature mFeature;
QMap< QString, QString > mAttributes;
QMap< QString, QString > mDerivedAttributes;
QMap< QString, QVariant > mParams;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
主要关注几个成员字段, 它们定义了识别返回结果的形式。
同时这个类中还有很多方法是属性识别功能的实现。看到这里,我想思路就有了,要实现属性识别的功能,需要继承这个类,并重写鼠标事件的方法来处理识别的返回结果结构体就行了。
这个类专门为矢量数据的属性识别功能而设计,不能处理栅格图层的属性。实际上,这个类正是体现了上面提到的思路。它继承自 QgsMapTool 类,并且从它的重写的鼠标事件实现代码中,可以看到它处理识别返回结果的方式。
void QgsMapToolIdentifyFeature::canvasReleaseEvent( QMouseEvent* e )
{
QgsPoint point = mCanvas->getCoordinateTransform()->toMapCoordinates( e->x(), e->y() );
QList<IdentifyResult> results;
if ( !identifyVectorLayer( &results, mLayer, point ) )
return;
emit featureIdentified( results[0].mFeature );
emit featureIdentified( results[0].mFeature.id() );
}
可以看到,它的处理事件发射了两个信号,分别将选中的要素和要素的id作为参数传了出去。但是它并没有直接处理。
如果要显示一个类似QGis属性识别的窗口,还需要我们自己定义一个类,然后通过 connect 的形式,将这里发射的 signal 与相应的 slot 方法连接。
这个类是QGis定义在qgis_app中定义的,并没有直接提供在二次开发API中,原理同 QgsMapToolIdentifyFeature 大致相同,也是继承自 QgsMapToolIdentify 类,并重写鼠标事件处理识别结果。
这个类在处理结果的时候调用了一个叫 QgsIdentifyResultsDialog 的类,这是QGis属性识别窗口的定义。
若想直接使用 QgsIdentifyResultsDialog 这个窗口,需要将相应的 .ui 文件以及窗口类文件拷贝到自己的工程中,并调用窗口类的初始化以及显示方法。
实现方法
讲到这里,我们要实现属性识别功能,就有了两种办法了:
- 定义方法接收 QgsMapToolIdentifyFeature 类发射的信号,处理属性识别返回的结果
- 模仿 QgsMapToolIdentifyAction 类的做法,定义一个类继承自 QgsMapToolIdentify, 并在重写的鼠标事件中处理属性识别返回的结果
第一种方法只能处理矢量图层的识别,第二种方法可以自定义扩展栅格以及其他图层的属性识别。
由于两种方法实际上在处理识别返回结果的地方原理是一样的,因此下面只示例第二种方法的实现。
首先定义一个类,继承自 QgsMapToolIdentify,并重写鼠标释放的事件。
class qgis_devMapToolIdentifyAction : public QgsMapToolIdentify
{
Q_OBJECT
public:
qgis_devMapToolIdentifyAction( QgsMapCanvas * canvas );
~qgis_devMapToolIdentifyAction();
virtual void canvasReleaseEvent( QMouseEvent * e );
};
鼠标释放事件的实现代码如下。
void qgis_devMapToolIdentifyAction::canvasReleaseEvent( QMouseEvent * e )
{
IdentifyMode mode = QgsMapToolIdentify::LayerSelection;
QList<IdentifyResult> results = QgsMapToolIdentify::identify( e->x(), e->y(), mode );
if ( results.isEmpty() )
{
qgis_dev::instance()->statusBar()->showMessage( tr( "No features at this position found." ) );
}
else
{
IdentifyResult feature = results.at( 0 );
QString title = feature.mLayer->name();
QString content = feature.mFeature.attribute( 1 ).toString();
QMessageBox::critical( NULL,
title,
content );
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
这个代码的效果如下所示:
![这里写图片描述](https://img-blog.csdn.net/20160204202908935)
![这里写图片描述](https://img-blog.csdn.net/20160204202847950)
![这里写图片描述](https://img-blog.csdn.net/20160204202544368)
最后
这个功能并不复杂,但通过对这个功能的实现方法的探讨,可以帮助我们后期开发的时候更合理的做一些自己想要的功能扩展。例如,你可以接收到属性返回的结果,并根据要素中的某个特殊属性,显示相应的动画。还可以根据属性的值的大小,设计显示不同高度的柱状图,不同比例的柱状图等等。
最后,感谢阅读。