QGis二次开发基础 — 文字标注

Label 这个功能是为了更直观的将矢量数据的属性信息显示在地图上,方便用户在浏览地图的时候查看。几乎每个 Gis 系统都会有这个基础功能,QGis 的 Label 更是非常漂亮,不仅支持标注的轮廓缓冲区功能,还可以给标注绘制上阴影效果等。今天就来说一说 QGis 二次开发中的文字标注功能的实现。

这里写图片描述


在这里补充一点:如果你的 QGis 显示出的矢量图层中文是乱码,不论是属性表里面的乱码还是 Label 上显示的乱码,都是字符编码的问题。
照下面的图,在图层属性里面,打开“通用”面板,设置“数据源编码”为“GBK”或者“GBK2312”就可以了。

这里写图片描述


本文包含两个部分,第一是直接实现 Label 功能,第二则是教大家直接把 QGis 的 Label 配置面板拿过来用。

实现思路

首先我们注意到,QgsVectorLayer 这个类当中,有一个 enableLabels() 方法。先好奇的将它设置为 true.

QgsVectorLayer* layer = (QgsVectorlayer*)this->activeLayer(); // 替换成任何一种获取矢量图层的方式
layer->enableLabels(true);
m_mapCanvas->refresh();
  •  

发现有如下的效果:

这里写图片描述

OK,Done!谢谢阅读。


哈哈,开个玩笑。
上图所示的结果,仅仅是打开了 Label 这个功能,它非常简单,但是到现在还没有任何配置。例如,我们要显示的字段,显示出来的样子等等。

可是我们确实已经实现了标注的功能,即在地图界面中显示出了文字信息。

于是我们发现,重点应该是如何配置这个标注的显示。

QgsPalLayerSettings

如果某个对象有一大堆属性需要设置,最好的方式就是新建另一个对象,来负责设置这些属性。QgsPalLayerSettings 这个类正是这样的存在,它负责矢量图层的相关设置。看一看 API 文档,不难发现,这里面定义的几乎全都是属性。

我相信配置属性对于大家来说都不会是一个难点,无非就是给不同的属性赋上不同的值而已。下面我会通过代码注释,来直接给出使用方法。

// 首先是定义一个 QgsPalLayerSettings 变量,并启用他的属性设置
QgsPalLayerSettings layerSettings;
layerSettings.enabled = true;

// 然后就可以开始根据API文档中的属性,进行自定义配置了
layerSettings.fieldName = layer->pendingFields()[3].name(); // 设置Label图层
layerSettings.centroidWhole = true; // 设置位置参考的中心点

// Label 字体设置
layerSettings.textColor = QColor( 0, 0, 0 ); // 设置字体颜色
layerSettings.textFont = QFont( "Times", 12 ); // 设置字体和大小

// Label 轮廓buffer设置
layerSettings.bufferDraw = true;
layerSettings.bufferColor = QColor( 255, 0, 0 ); // 轮廓buffer的颜色
layerSettings.bufferSize = 1; // 轮廓buffer大小
layerSettings.bufferTransp = 0.5; // 轮廓buffer的透明度

// Label 阴影绘制
layerSettings.shadowDraw = true;
layerSettings.shadowOffsetAngle = 135; // 阴影的角度
layerSettings.shadowOffsetDist = 1; // 阴影与Label的距离

QgsVectorLayer* layer = (QgsVectorlayer*)this->activeLayer(); // 替换成任何一种获取矢量图层的方式

layerSettings.writeToLayer( layer ); // 将配置写入图层
m_mapCanvas->refresh();

注意,最后一定要用 writeToLayer() 方法把设置写到相应的图层中。上面的代码效果如下图:

这里写图片描述

至于想要更特别的显示效果,就自己去找相应的属性进行设置就可以了。

数据定义的标注样式

这里再多说一点,QgsPalLayerSettings 的定义中,第一个变量就是叫 DataDefinedProperties 的枚举类型,这个枚举类型里面定义的属性,其实在 QgsPalLayerSettings 的公共属性中是对应的,那么它的作用是什么呢?其实,正如它的名字那样,DataDefinedProperties 是由数据定义的属性,也就说,同一个图层的属性可以是动态变化的。比如说同一个图层中,有两个 Feature,想要将这两个 Feature 的 Label 显示为不同大小,甚至不同的颜色、字体等。就可以定义一个 size 的字段来存储大小,color 的字段存储颜色, font 字段存储字体。这样,这两个 Feature 显示出来的 Label 样式就会不一样,并且是跟随数据的改变而改变的。

还是通过实际操作来解释会更直观一点。

现在我们新增加 3 个字段到测试图层中。注意我们新增的 color 表示为十六进制的颜色字符串,并且为了显示方便,在这个测试图层中,我们仅保留了 3 个要素。

这里写图片描述

我们在 QGis 的 Label 属性面板中,做如下的设置

这里写图片描述

这里写图片描述

这里写图片描述

点击确定,就可以得到如下图所示的效果,即这 3 个要素显示的 Label 样式各不相同。

这里写图片描述

那么,怎么通过代码来实现这个功能呢?我们注意到,QgsPalLayerSettings 类中,包含了一个 setDataDefinedProperty 方法,定义如下:

这里写图片描述

使用这个方法,我们就可以达到以上 QGis 中实现的 Label 不同样式的功能了。测试代码如下:

// 同样定义一个 QgsPalLayerSettings 变量,并启用他的属性设置
QgsPalLayerSettings layerSettings;
layerSettings.enabled = true;

// 设置显示字段
layerSettings.fieldName = layer->pendingFields()[3].name(); 

layerSettings.setDataDefinedProperty( layerSettings.Size, true, false, NULL, "size" ); // 设置 Label 大小
layerSettings.setDataDefinedProperty( layerSettings.Color, true, false, NULL, "color" ); // 设置 Label 颜色
layerSettings.setDataDefinedProperty( layerSettings.Family, true, false, NULL, "font" ); // 设置 Label 字体

QgsVectorLayer* layer = (QgsVectorlayer*)this->activeLayer(); // 替换成任何一种获取矢量图层的方式

layerSettings.writeToLayer( layer ); // 将配置写入图层
m_mapCanvas->refresh();

运行这段代码会得到与上面 QGis 中显示相同的效果。

复制 QGis 标注配置面板

配置代码主要都是重复性劳动,还要做 UI 显示,比较复杂。如果你对 UI 没有特别的要求,自然就想到直接用 QGis 的配置面板吧。这个也比较 easy,也就是复制源码中相关的文件到自己的工程,然后调用就可以了。这几个文件分别是:

  • qgslabelpreview.h 以及 qgslabelpreview.cpp
  • qgslabelinggui.h 以及 qgslabelinggui.cpp
  • qgsmapunitscaledialog.ui
  • widget_svgselector.ui
  • qgsunitselectionwidget.ui
  • qgsmapunitscaledialog.ui
  • qgslabelingguibase.ui
  • qgsexpressionbuilder.ui
  • qgscharacterselectdialogbase.ui
    (希望我没有漏掉文件)

其中, qgslabelinggui 类就是我们可以直接调用的窗口,它的 UI 文件为 qgslabelingguibase.ui 。我们只需要新建一个 QDialog 窗口,并新建一个 qgslabelinggui 类放入这个窗口,然后调用它的 init 方法和 QDialog 的 show 方法就可以了。

QgsVectorLayer* layer = (QgsVectorlayer*)this->activeLayer(); // 替换成任何一种获取矢量图层的方式

QDialog *dialog = new QDialog( this );
QDialogButtonBox* btnBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
QVBoxLayout* hlayout = new QVBoxLayout( dialog );

QgsLabelingGui labelingGui = new QgsLabelingGui( layer, m_mapCanvas, dialog ); // 根据自己的情况传入不同的参数
labelingGui->init();

hlayout->addWidget( labelingGui );
hlayout->addWidget( btnBox );
connect( btnBox, SIGNAL( accepted() ), labelingGui, SLOT( apply() ) );
connect( btnBox, SIGNAL( rejected() ), dialog, SLOT( close() ) );
dialog->show();

运行代码,就会得到如下的视图,可以配置 Label 的显示样式了。

这里写图片描述

源代码

最后,给出用到的两个主要方法的全部源代码。(或者你也可以直接去 Github 下载我的所有示例源代码,链接见文末。)

void qgis_dev::testVecLayerLabel()
{
    QgsVectorLayer* layer = ( QgsVectorLayer* )this->activeLayer();
    if ( layer == NULL || layer->isValid() == false ) { return; }

    // 首先是定义一个 QgsPalLayerSettings 变量,并启用他的属性设置
    QgsPalLayerSettings layerSettings;
    layerSettings.enabled = true;

    // 然后就可以开始根据API文档中的属性,进行自定义配置了
    layerSettings.fieldName = layer->pendingFields()[3].name(); // 设置Label图层
    layerSettings.centroidWhole = true; // 设置位置参考的中心点

    // Label 字体设置
    layerSettings.textColor = QColor( 0, 0, 0 ); // 设置字体颜色
    layerSettings.textFont = QFont( "Times", 12 ); // 设置字体和大小

    // Label 轮廓buffer设置
    layerSettings.bufferDraw = true;
    layerSettings.bufferColor = QColor( 255, 0, 0 ); // 轮廓buffer的颜色
    layerSettings.bufferSize = 1; // 轮廓buffer大小
    layerSettings.bufferTransp = 0.5; // 轮廓buffer的透明度

    // Label 阴影绘制
    layerSettings.shadowDraw = true;
    layerSettings.shadowOffsetAngle = 135; // 阴影的角度
    layerSettings.shadowOffsetDist = 1; // 阴影与Label的距离

    layerSettings.fieldName = layer->pendingFields()[3].name(); // 设置Label图层
    layerSettings.setDataDefinedProperty( layerSettings.Size, true, false, NULL, "size" );
    layerSettings.setDataDefinedProperty( layerSettings.Color, true, false, NULL, "color" );
    layerSettings.setDataDefinedProperty( layerSettings.Family, true, false, NULL, "font" );

    layerSettings.writeToLayer( layer ); // 将配置写入图层
    m_mapCanvas->refresh();
}
void qgis_dev::testUseLabelConfigDialog()
{
    QgsVectorLayer* layer = ( QgsVectorLayer* )this->activeLayer();
    if ( layer == NULL || layer->isValid() == false ) { return; }

    QDialog *dialog = new QDialog( this );
    QDialogButtonBox* btnBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
    QVBoxLayout* hlayout = new QVBoxLayout( dialog );

   QgsLabelingGui labelingGui = new QgsLabelingGui( layer, m_mapCanvas, dialog ); // 根据自己的情况传入不同的参数
    labelingGui->init();

    hlayout->addWidget( labelingGui );
    hlayout->addWidget( btnBox );
    connect( btnBox, SIGNAL( accepted() ), labelingGui, SLOT( apply() ) );
    connect( btnBox, SIGNAL( rejected() ), dialog, SLOT( close() ) );
    dialog->show();
}

先写到这里吧,谢谢大家阅读!

© 版权声明
THE END
喜欢就支持一下吧
点赞914 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容