Qgis 开发初级 《数据库和图层》

Qgis 可以加载很多数据源,像shapefile 文件, gdb文件等,还可以直接链接企业数据库。在这里,我只介绍比较常用的本地数据库 gdb。gdb 是esri 开发的数据库,gdb数据库不是开源的,qigs用了OpenFileGdb的驱动读写的,这个驱动目前是支持读写的。Qgis 读写数据库做了一层封装的,读写都是通过Qgis 的图层类实现,所以这里和图层一起介绍。

1、数据库操作

1.1、数据库的读取

读取数据库里面的某个表,从数据库的表到Qgis的QgsVectorLayer 矢量图层类中。参数表示 读取 D盘 test.gdb 的 名为 point 的表。注意这里 |layername= 附近 不要有空格,否则可能会读取失败。

QString layerName="point";
QString dbPath="D:\\test.gdb"+"|layername="+layerName;
QgsVectorLayer* gdbLayer = new QgsVectorLayer(dbPath, layerName, QStringLiteral("ogr"));

1.2、数据库的写入

这里会将QgsVectorLayer 写入到gdb中。

QgsVectorFileWriter::SaveVectorOptions options;
options.actionOnExistingFile = QgsVectorFileWriter::AppendToLayerNoNewFields;
options.driverName = "openFileGDB";
options.layerName = layerName;

QgsCoordinateTransform ct;
ct = QgsCoordinateTransform(vectorlayer->crs(), vectorlayer->crs(), vectorlayer->transformContext());
ptions.ct = ct;
QgsVectorFileWriter::writeAsVectorFormatV3(vectorlayer, cds.workSpacePath(), vectorlayer->transformContext(), options);

其中QgsVectorFileWriter::AppendToLayerNoNewFields是个枚举值。 通过它来确定数据库记录是覆盖还是增加。

  enum ActionOnExistingFile
    {
      //! Create or overwrite file
      CreateOrOverwriteFile,

      //! Create or overwrite layer
      CreateOrOverwriteLayer,

      //! Append features to existing layer, but do not create new fields
      AppendToLayerNoNewFields,

      //! Append features to existing layer, and create new fields if needed
      AppendToLayerAddFields
    };

2、图层的操作

Qgis的图层是Qgis封装的通用类,Qgis 用它屏蔽了数据源的不同,使用它可以操作所有的数据源。数据库的字段和记录都是通过Qgis的图层来实现的。Qgis 里面有各种类型的图层,跟gdb 有关的主要是QgsVectorLayer的矢量图层。

2.1、 图层的新建和添加

        图层第一种创建方式就是从数据库读取的图层。这个时候图层是与数据库表同步的。获取方法参考 1.1 数据库的读取。

        图层第二种创建是直接创建内存图层, 创建一个名字叫 test的点图层,并设置坐标系。

QgsVectorLayer* layer=new QgsVectorLayer("PointZ", "test", "memory");
layer->setCrs(QgsProject::instance()->crs());

这种创建的第一个参数是Qgis 的类型,该类型是个枚举,点,线,面都可以创建,枚举如下,创建的时候直接写字符串就可以。

enum Type
    {
      Unknown = 0,
      Point = 1,
      LineString = 2,
      Polygon = 3,
      Triangle = 17,
      MultiPoint = 4,
      MultiLineString = 5,
      MultiPolygon = 6,
      GeometryCollection = 7,
      CircularString = 8,
      CompoundCurve = 9,
      CurvePolygon = 10, //13, //should be 10. Seems to be correct in newer PostGIS versions
      MultiCurve = 11,
      MultiSurface = 12,
      NoGeometry = 100, //attributes only
      PointZ = 1001,
      LineStringZ = 1002,
      PolygonZ = 1003,
      TriangleZ = 1017,
      MultiPointZ = 1004,
      MultiLineStringZ = 1005,
      MultiPolygonZ = 1006,
      GeometryCollectionZ = 1007,
      CircularStringZ = 1008,
      CompoundCurveZ = 1009,
      CurvePolygonZ = 1010,
      MultiCurveZ = 1011,
      MultiSurfaceZ = 1012,
      PointM = 2001,
      LineStringM = 2002,
      PolygonM = 2003,
      TriangleM = 2017,
      MultiPointM = 2004,
      MultiLineStringM = 2005,
      MultiPolygonM = 2006,
      GeometryCollectionM = 2007,
      CircularStringM = 2008,
      CompoundCurveM = 2009,
      CurvePolygonM = 2010,
      MultiCurveM = 2011,
      MultiSurfaceM = 2012,
      PointZM = 3001,
      LineStringZM = 3002,
      PolygonZM = 3003,
      MultiPointZM = 3004,
      MultiLineStringZM = 3005,
      MultiPolygonZM = 3006,
      GeometryCollectionZM = 3007,
      CircularStringZM = 3008,
      CompoundCurveZM = 3009,
      CurvePolygonZM = 3010,
      MultiCurveZM = 3011,
      MultiSurfaceZM = 3012,
      TriangleZM = 3017,
      Point25D = 0x80000001,
      LineString25D,
      Polygon25D,
      MultiPoint25D,
      MultiLineString25D,
      MultiPolygon25D
    };

这种方式再介绍下指定地方坐标系的情况,str 填写的是坐标系的 wkt 字符串。其中layer 是QgsVectorLayer * 的指针对象。

QString str = "";
QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromWkt(str);
layer->setCrs(crs);

这两种图层创建后,其实还没加入到Qgis 工程中。通过QgsProject 加入到工程中。代码如下。

QgsProject::instance()->addMapLayer(layer);

加入到工程中的图层,可以通过名字或者ID 重新获取指针。下面代码是获取名字叫test的图层。mapLayersByName 返回的是QList 列表对象。因为Qgis 多个图层可以叫同一个名字。当然可以通过layer的id获取,再次不多介绍了。

QgsVectorLayer* layer = qobject_cast<QgsVectorLayer*>(QgsProject::instance()->mapLayersByName("test").at(0));

2.2、 图层的删除

其中di 是 图层QgsVectorLayer * 的指针对象 的图层id; 这个里面会自动删除指针对象。

 QgsProject::instance()->removeMapLayer(id);

2.3、图层的显隐

这里需要介绍下图层显隐控制,图层的显隐控制不是在图层级别的,是与图层的控件有关系的。比如根据某个图层id来隐藏图层。

QgsLayerTreeView* layerTreeView = interface->layerTreeView();
QgsLayerTreeLayer* nodeLayer = layerTreeView ->layerTreeModel()->rootGroup()->findLayer(layer->id());
nodeLayer->setItemVisibilityChecked(false);

2.4、图层的重绘

这是一个很重要的操作,可以单独刷新某个图层,有些操作如果不调用这个方法时看不到新增要素的。

layer->triggerRepaint()

2.5、添加字段

Qgis 添加字段涉及到 QgsField 的类,如下代码,添加了一个String 类型的 Name 字段,一个Double类型的X 字段和一个Double类型的Y字段。Qgis 是用Qt 开发,经常会用到一些qt的语法。其中layer 是QgsVectorLayer * 的指针对象。

QList<QgsField> fieldList;
fieldList.append(QgsField("Name", QVariant::String));
fieldList.append(QgsField("X", QVariant::Double));
fieldList.append(QgsField("Y", QVariant::Double));

QgsVectorDataProvider* provider=layer->dataProvider();
provider->addAttributes(fieldList);
layer->updateFields();

3 、要素操作

图层和字段创建好了后,就需要对要素做进一步操作。主要实现增删改查,图层的要素编辑其实有两种,一种在编辑模式下,一种不在编辑模式下。两种的区别是编辑模式考虑的用户操作体验,可以做回撤,重做等操作,批处理时,效率相对较低。非编辑模式下直接操作同步数据库,效率比较高。这里先介绍下非编辑模式。Qgis的要素类是QgsFeature。 代表着数据库中的一行记录。

3.1 、添加要素

添加要素的代码如下,首先,创建QgsFature对象,其中layer 是QgsVectorLayer * 的指针对象。

QgsFeature feature = QgsFeature(layer->fields());
QgsVectorDataProvider* provider = layer->dataProvider();
provider->addFeature(feature );

也可以用addFeatures 批量添加,这种方式应该更快一些。QList 可以换成QgsFeatureList 是一样,QgsFeatureList 是QList<QgsFeature> 的宏命令。

QList<QgsFeature> features;
QgsFeature feature = QgsFeature(layer->fields());
features.push_back(feature );
QgsVectorDataProvider* provider = layer->dataProvider();
provider->addFeatures(features);

添加几何字段值,在平面坐标系下这样操作。在setGeometry 的参数是QgsGeometry。这个对象的具体操作会在后面介绍。注意,添加的几何,要与创建的图层一致,图层是点,添加点,图层是线,添加线,

feature.setGeometry(QgsGeometry::fromPointXY(QgsPointXY(1000, 1000)));

添加字段属性值,单个添加。

feature.setAttribute("X", 1000);
feature.setAttribute("Y", 1000);

也可以批量添加属性。但是要注意属性的顺序。

feature.setAttributes(QgsAttributes() << "Point 1");

3.2、要素更新

要素更新,主要更新地理字段和属性字段。可以使用下面的方法批量添加地理字段和属性字段。其中index 是字段的所在字段索引,id 是QgsFeature的 id ,layer  是 QgsVectorLayer * 的指针对象。 类型是QgsFeatureId。其中geo 和 amap 的不在举例子了。

	QgsGeometry geo;
    QgsAttributeMap amap;
    amap.insert(index, 1000);

    QgsChangedAttributesMap camap;
	QgsGeometryMap geoMap;
    camap.insert(id,amap);
    geoMap.insert(id,geo);

	layer->dataProvider()->changeFeatures(camap, geoMap);

3.3、要素删除

        通过以下方法删除要素

QgsFeatureIds ids;
layer->dataProvider()->deleteFeatures(ids);

4、编辑模式

编辑模式需要单独拿出来说一下,要素的增删改查可以在编辑模式下执行,在非编辑模式下做增删改查更快,更在乎的是效率,编辑模式相对慢一些,因为它需要更多的流程,整个编辑过程是可以追溯的,可撤销,可恢复,编辑模式一般是交互式的增删改查,更在乎的是用户体验。其中设计到layer 都认为是QgsVectorLayer 的对象指针。

4.1、开启和停止

一般来说,编辑模式都是针对的矢量图层。

layer->startEditing();

停止编辑并保存要素是一个操作,如果想保存继续编辑,还需要调用一次开启编辑。

layer->commitChanges(); 

判断是否开启编辑

layer->isEditable()

4.2、事务

下面的两个命令也是非常重要,在编辑模式下非常重要的就是回退,在beginEditCommand和endEditCommand的任何增删改查操作在图层回退或者重做时会被认为一次操作。

layer->beginEditCommand("当前图层");
layer->endEditCommand();

4.3、增加要素

要素增加,和非编辑下就一行代码不一样,实在图层级别直接添加。如下

layer->addFeature(feature)

4.4、要素更新

修改要素,其中id是QgsFeatureId。

QgsGeometry geo;
QgsAttributeMap amap;
layer->changeGeometry(id, geo);
layer->changeAttributeValues(id, amap);

4.5、要素删除

其中id是QgsFeatureId。

layer->deleteFeature(id)

4.6、要素的回退和重做

回退

auto undostack = layer->undoStack();
undostack->redo();

重做

auto undostack = layer->undoStack();
undostack->undo();

5、小节

        我们介绍了数据库的操作,图层和要素的操作,其中图层和要素在非编辑模式下的操作,也是比较高效率的操作。编辑模式下的操作其实类似,编辑模式其实是与后面的交互一起使用,只有这样才能实现通过交互来增加要素,后面会介绍qgis的交互事件。

要实现在QGIS二次开发中,当QgsLayerTreeGroup的复选框选中时,联动选中其所有子节点,可以按照以下步骤进行操作: 1. 获取QgsLayerTreeGroup对象 首先,需要获取到要操作的QgsLayerTreeGroup对象。可以通过QgsProject类的`layerTreeRoot()`方法获取整个图层树的根节点,然后通过遍历找到目标QgsLayerTreeGroup对象。例如: ```cpp QgsLayerTreeGroup* rootGroup = QgsProject::instance()->layerTreeRoot(); QgsLayerTreeGroup* targetGroup = nullptr; // 遍历图层树,找到目标QgsLayerTreeGroup对象 for (QgsLayerTreeNode* node : rootGroup->findLayers()) { if (node->type() == QgsLayerTreeNode::Group && node->name() == "Target Group") { targetGroup = dynamic_cast<QgsLayerTreeGroup*>(node); break; } } if (!targetGroup) { // 目标QgsLayerTreeGroup对象不存在,进行错误处理 return; } ``` 2. 监听复选框状态变化 使用QObject::connect()方法连接QgsLayerTreeGroup对象的`visibilityChanged()`信号与自定义槽函数,以监听复选框状态的变化。当复选框状态变化时,会自动调用槽函数。例如: ```cpp // 连接visibilityChanged信号与自定义槽函数 QObject::connect(targetGroup, &QgsLayerTreeGroup::visibilityChanged, this, &MyClass::onGroupVisibilityChanged); ``` 3. 实现槽函数 在槽函数中,可以遍历目标QgsLayerTreeGroup对象的所有子节点,并设置它们的复选框状态与目标QgsLayerTreeGroup对象一致。例如: ```cpp void MyClass::onGroupVisibilityChanged() { // 获取目标QgsLayerTreeGroup对象的复选框状态 bool groupVisible = targetGroup->isVisible(); // 遍历目标QgsLayerTreeGroup对象的所有子节点 for (QgsLayerTreeNode* node : targetGroup->findLayers()) { // 设置子节点的复选框状态与目标QgsLayerTreeGroup对象一致 node->setItemVisibilityChecked(groupVisible); } } ``` 通过以上步骤,当目标QgsLayerTreeGroup对象的复选框状态变化时,其所有子节点的复选框状态将会联动选中或取消选中。请根据实际情况修改代码中的参数和逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhangiser

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值