QGIS开发笔记(五):qgis加载标记点功能,基础标记数量与性能对比测试

前言

对地图增加标记点、标记图标、线条、图形等等,都是常规通用操作,本篇先实现添加标记点,然后对比点数量性能,同时由于像素大小对性能也有较大印象,测试了1、2像素超大数量绘图时,拽托性能与显示效果。

Demo

1000标记点1像素大小

Cpu和内存大小:

拽托缩放不间断操作的CPU和内存大小:

10000标记点1像素大小

1000标记点2像素大小

10000标记点2像素大小

QsgVectorLayer

概述

QgsVectorLayer是QGIS中用于处理矢量数据的核心类,它负责加载、管理和操作各种矢量空间数据(点、线、面等几何类型)。

其作为矢量数据的容器,连接数据源(如 Shapefile、PostGIS、GeoJSON 等)并提供数据访问接口。加载矢量数据、管理属性表、处理几何对象、实现空间查询等。继承自QgsMapLayer,与QgsRasterLayer共同构成QGIS中两种主要的图层类型。

数据源连接字符串

创建QgsVectorLayer时需要指定数据源字符串(data source string),格式因数据格式而异:

常用驱动(Provider)

注意事项

内存管理

QgsVectorLayer对象通常由QgsProject管理,调用 QgsProject::addMapLayer后无需手动删除。

非线程安全

QGIS 核心类(包括QgsVectorLayer)不保证线程安全,避免在非主线程中直接操作。

性能优化

  • 对大数据集使用空间索引(layer->createSpatialIndex())。
  • 批量操作时使用QgsVectorLayerUtils::addFeatures()替代循环添加。
  • 避免频繁调用 commitChanges(),建议批量修改后一次性提交。
      通过QgsVectorLayer,可以在C++中实现QGI 几乎所有矢量数据处理功能,结合QgsGeometry、QgsFeature 等类可完成复杂的空间分析任务。

常用属性和操作

构造与初始化

通过数据源字符串(URI)、图层名、数据提供者类型构造 QgsVectorLayer。

cpp 复制代码
// 加载 Shapefile
QString uri = "/path/to/data.shp";
QgsVectorLayer* layer = new QgsVectorLayer(uri, "my_layer", "ogr"); if (!layer->isValid()) { // 处理加载失败 } // 添加到地图画布 QgsProject::instance()->addMapLayer(layer); 

图层基本信息

  • geometryType():返回图层几何类型(QgsWkbTypes::GeometryType)。
  • crs():返回图层坐标系(QgsCoordinateReferenceSystem)。
  • fields():返回属性字段集合(QgsFields)。
  • featureCount():返回要素总数。
cpp 复制代码
// 图层名称
QString name = layer->name(); // 几何类型(点/线/面等) QgsWkbTypes::Type geomType = layer->wkbType(); QString geomTypeName = QgsWkbTypes::displayString(geomType); // 如 "Point" // 坐标参考系(CRS) QgsCoordinateReferenceSystem crs = layer->crs(); QString crsId = crs.authid(); // 如 "EPSG:4326" // 要素总数 int featureCount = layer->featureCount(); // 空间范围 QgsRectangle extent = layer->extent(); 

属性表操作

属性表由QgsFields管理,每个字段对应QgsField:

cpp 复制代码
// 获取所有字段
const QgsFields& fields = layer->fields(); // 遍历字段 for (int i = 0; i < fields.count(); ++i) { const QgsField& field = fields.at(i); qDebug() << "字段名:" << field.name() << "类型:" << field.typeName(); } // 根据名称查找字段索引 int fieldIndex = fields.indexOf("population"); 

要素迭代与访问

通过QgsFeatureIterator遍历要素:

cpp 复制代码
// 获取要素迭代器(默认遍历所有要素)
QgsFeatureIterator it = layer->getFeatures(); QgsFeature feature; while (it.nextFeature(feature)) { // 要素 ID QgsFeatureId fid = feature.id(); // 几何对象(转为 WKT 字符串) QgsGeometry geom = feature.geometry(); QString wkt = geom.asWkt(); // 属性值(通过索引或字段名访问) QVariant population = feature.attribute("population"); // 字段名 QVariant id = feature.attribute(0); // 字段索引 qDebug() << "FID:" << fid << "几何:" << wkt << "人口:" << population.toInt(); } 

查询与过滤

cpp 复制代码
// 1. 属性查询(例如:population > 100000)
QgsExpression expr("\"population\" > 100000"); if (expr.hasParserError()) { qWarning() << "表达式错误:" << expr.parserErrorString(); } QgsFeatureRequest request(expr); QgsFeatureIterator attrIt = layer->getFeatures(request); // 2. 空间查询(例如:在指定范围内的要素) QgsRectangle rect(116.0, 39.0, 117.0, 40.0); // 北京附近范围 QgsFeatureRequest spatialRequest; spatialRequest.setFilterRect(rect); QgsFeatureIterator spatialIt = layer->getFeatures(spatialRequest); 

编辑要素

cpp 复制代码
// 开启编辑
if (!layer->startEditing()) { qWarning() << "无法开启编辑模式"; return; } // 1. 添加新要素 QgsFeature newFeature(layer->fields()); newFeature.setGeometry(QgsGeometry::fromPointXY(QgsPointXY(116.4, 39.9))); // 点坐标 newFeature.setAttribute("name", "Beijing"); newFeature.setAttribute("population", 21540000); if (!layer->addFeature(newFeature)) { qWarning() << "添加要素失败"; } // 2. 更新现有要素 QgsFeature feature; if (layer->getFeature(1, feature)) { // 获取 ID=1 的要素 feature.setAttribute("population", 22000000); if (!layer->updateFeature(feature)) { qWarning() << "更新要素失败"; } } // 3. 删除要素 if (!layer->deleteFeature(2)) { // 删除 ID=2 的要素 qWarning() << "删除要素失败"; } // 提交编辑(保存修改) if (!layer->commitChanges()) { qWarning() << "提交修改失败:" << layer->commitErrors(); layer->rollBack(); // 回滚 } else { qDebug() << "修改已保存"; } 

样式设置

cpp 复制代码
// 从 QML 文件加载样式
if (!layer->loadNamedStyle("/path/to/style.qml")) { qWarning() << "加载样式失败"; } layer->triggerRepaint(); // 刷新图层 

Demo源码

QGisManager.cpp

cpp 复制代码
bool QGisManager::addMakerPointToCanvas(QgsMapCanvas * pMapCanvas, double x, double y, QString baseName, QString name, QString crs, QColor color, int size) { // 步骤一:创建点图层 QgsVectorLayer *pMarkerLayer = new QgsVectorLayer( QString("Point?crs=%1").arg(crs), // 坐标系 name, // 名称 "memory"); if(!pMarkerLayer->isValid()) { LOG << "Failed to" << "new QgsVectorLayer(" << QString("Point?crs=%1").arg(crs) << "," << name << "," << "memory)"; return false; } // 步骤二:添加属性字段,用于存储标记名称 QgsField nameFiled(baseName, QVariant::String); pMarkerLayer->dataProvider()->addAttributes({nameFiled}); pMarkerLayer->updateFields(); // 步骤三:创建一个点要素 QgsFeature feature; feature.setGeometry(QgsGeometry::fromPointXY(QgsPointXY(x, y))); feature.setAttributes(QgsAttributes({name})); // 步骤四:将要素添加到图层 pMarkerLayer->dataProvider()->addFeature(feature); pMarkerLayer->updateExtents(); // 步骤五:创建配置标记符号 QgsMarkerSymbol * pMarkerSymbol = new QgsMarkerSymbol(); pMarkerSymbol->setColor(color); pMarkerSymbol->setSize(size); // 步骤六:设置图层渲染器 QgsSingleSymbolRenderer * pSingleSymbolRenderer = new QgsSingleSymbolRenderer(pMarkerSymbol); pMarkerLayer->setRenderer(pSingleSymbolRenderer); // 步骤七:添加到地图 QList<QgsMapLayer *> listMapLayer = pMapCanvas->layers(); if(!listMapLayer.contains(pMarkerLayer)) { listMapLayer.push_front(pMarkerLayer); } pMapCanvas->setLayers(listMapLayer); //pMapCanvas->refresh(); return true; } 

QGisWidget.cpp

cpp 复制代码
void QGisWidget::test_demo3(QString filePath, int pointNum) { // 步骤一:创建画布层 QgsMapCanvas *pMapCanvas = new QgsMapCanvas(); // 启用并行渲染 pMapCanvas->setParallelRenderingEnabled(true); // 强制始终允许渲染 pMapCanvas->setRenderFlag(true); // 步骤二:创建图片数据影像层 QgsRasterLayer *pLayer = new QgsRasterLayer(filePath); if(!pLayer->isValid()) { LOG << "Failed to load:" << filePath; return; } // 步骤三:设置图层进入画布 pMapCanvas->setLayers({pLayer}); // 并不会影响QgsProject::instance()->mapLayers()的数量 // 步骤四:设置画布范围 pMapCanvas->setExtent(pLayer->extent()); // 步骤五:刷新画布 pMapCanvas->refresh(); // 步骤六:需要拽托移动,则需要QgsMapToolPan,否则无法移动只能滚轮缩放 QgsMapToolPan *pMapToolPan = new QgsMapToolPan(pMapCanvas); pMapCanvas->setMapTool(pMapToolPan); // 步骤七:随机生成点,pointNum double step = 1; int col = 100; LOG << "add" << pointNum << "points," << "step:" << step << ", col:" << col; for(int index = 0; index < pointNum; index++) { QGisManager::addMakerPointToCanvas(pMapCanvas, index % col * step, index / col * step, "point", QString("point_%1").arg(index), "EPSG:3857", Qt::green, 1); LOG << (index % col * step) << (index / col * step) << QString("point_%1").arg(index); } // 布局初始化 QHBoxLayout *pHBoxLayout = dynamic_cast<QHBoxLayout *>(this->layout()); if(!pHBoxLayout) { pHBoxLayout = new QHBoxLayout(this); } pHBoxLayout->addWidget(pMapCanvas, 1); pHBoxLayout->setMargin(0); setLayout(pHBoxLayout); } 

工程模板v1.2.0