【QGIS二次开发】地图编辑-07
系列目录:
【QGIS二次开发】地图显示与交互-01_qgis二次开发加载地图案例-CSDN博客
【QGIS二次开发】地图显示与交互-02_setlayerlabeling-CSDN博客
【QGIS二次开发】地图符号与色表-03-CSDN博客
【QGIS二次开发】地图编辑-04-CSDN博客
【QGIS二次开发】地图编辑-05-CSDN博客
【QGIS二次开发】地图编辑-06-CSDN博客
4.13 剪断线、分割区
用户使用线/面要素分割工具时,需要先选中一个或多个要素,再点击分割的按钮,再在界面中点击多个点形成连续的线段,连续线段就是用户对选中要素进行裁剪的线,确认裁剪时点击鼠标右键,系统会计算裁剪后的图形保存在图层中。
未进行裁剪的线图层和鼠标点击用于裁剪的线如下图所示,蓝色的线是用户通过鼠标左键点击绘制的:
图 64 要素裁剪操作
点击右键裁剪后,要素被分成三部分,可以使用选择工具分别进行选中,如下图所示:
图 65 要素裁剪成功
进行面要素分割时,与线要素剪断一样,绘制一条线进行分割,分割的操作和结果分别如下图所示:
图 66 面要素裁剪
图 67 面要素裁剪成功
实现裁剪的代码如下所示,在鼠标左键或右键点击事件中,canvasPressEvent函数分别记录了点击位置的坐标,并根据鼠标左键或右键的不同进行相应处理。左键点击时,将点击坐标添加到裁剪点列表,并通过pRubBand绘制点;右键点击时,同样将点击坐标添加到裁剪点列表,若裁剪点数量达到两个以上,则调用clipFeature()函数对选中要素进行裁剪操作。在clipFeature()函数中,遍历选中要素,使用裁剪点列表裁剪线要素,然后将裁剪后的线要素替换原始要素,并将生成的新要素添加到图层中。
void QgsMapToolClipLine::canvasPressEvent(QgsMapMouseEvent* e)
{ // 检查是否是鼠标左键点击事件 if (e->button() == Qt::LeftButton) { double xc = e->x(); double yc = e->y(); // 得到当前坐标变换对象 const QgsMapToPixel* pTransform = m_mapCanvas->getCoordinateTransform(); // 转换成地图坐标 QgsPoint mPoint(pTransform->toMapCoordinates(xc, yc)); clipPoints.push_back(mPoint); pRubBand->addPoint(mPoint); 如果有两个点,绘制线段 //if (clipPoints.size() >= 2) { // // drawLine(clipPoints[clipPoints.size()-2], clipPoints[clipPoints.size() - 1]); //} } else if (e->button() == Qt::RightButton) { double xc = e->x(); double yc = e->y(); // 得到当前坐标变换对象 const QgsMapToPixel* pTransform = m_mapCanvas->getCoordinateTransform(); // 转换成地图坐标 QgsPoint mPoint(pTransform->toMapCoordinates(xc, yc)); pRubBand->addPoint(mPoint); clipPoints.push_back(mPoint); if (clipPoints.size() >= 2) { clipFeature(); pRubBand->reset(); //pRubBand->asGeometry(); } }
} void QgsMapToolClipLine::clipFeature() { QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { // 裁剪线 QgsGeometry geom = selectedFeature.geometry(); QgsPointSequence splitLine; for (int i = 0; i < clipPoints.size(); i++) { splitLine << clipPoints[i]; } QVector<QgsGeometry> newGeometries; QgsPointSequence topologyTestPoints; geom.splitGeometry(splitLine, newGeometries, true, topologyTestPoints); // 替换线 if (newGeometries.size() > 0) { m_currentlayer->startEditing(); // 启动编辑 for (const QgsGeometry& newGeom : newGeometries) { geom = geom.difference(newGeom); } QgsFeatureId id = selectedFeature.id(); m_currentlayer->changeGeometry(id, geom); //m_currentlayer->deleteFeature(id); // 创建新的特征 QgsFeatureList newFeatures; for (auto& newGeom : newGeometries) { QgsFeature newFeature; newFeature.setGeometry(newGeom); //QgsAttributes att = m_currentlayer->getFeature(id).attributes(); //newFeature.setAttributes(att); QgsFeatureId n = m_currentlayer->featureCount(); newFeature.setId(n); m_currentlayer->dataProvider()->addFeature(newFeature); //newFeatures.append(newFeature); } //m_currentlayer->deleteFeature(id); //transformedLayer->dataProvider(). } } //m_currentlayer->commitChanges(); clipPoints.clear(); m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers();
}
4.14 连接线、合并区
用户使用连接/合并要素工具时,可以使用按区域选择工具选中多个要素,按区域选择时通过鼠标拖动一个框选择要素,按区域选择使用效果如下:
图 68 示例数据
选中后点击合并要素按钮,系统自动合并要素,如果合并失败系统会自动返回警告框,合并成功后使用选择工具,合并后的要素会一起被选择。
图 69 按区域选择
实现合并要素的主要代码如下所示。通过迭代遍历选中的要素,将每个要素的几何体合并到初始化的combinedGeom中,然后删除原始要素。最后,创建一个新的要素,将合并后的几何体设置为其几何体,并通过数据提供者将新要素添加到当前图层中。在合并失败的情况下,会弹出错误消息提示框。
void QgsMapToolCombine::combineFeature()
{ QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; m_currentlayer->startEditing(); QgsGeometry combinedGeom; // 初始化一个空的几何体来保存合并的结果 QgsAttributes att; QgsFeatureId id; int num = 0; while (selectedFeatureIt.nextFeature(selectedFeature)) { num++; // 如果这是第一个要素,直接使用其几何体 if (num == 1) { combinedGeom = selectedFeature.geometry(); } else if (combinedGeom.isNull() && num != 1) { // 合并失败 QMessageBox::critical(nullptr, "Error", "合并失败"); return; } else { // 合并几何体 combinedGeom = combinedGeom.combine(selectedFeature.geometry()); } id = selectedFeature.id(); att = m_currentlayer->getFeature(id).attributes(); m_currentlayer->deleteFeature(id); } QgsFeature newFeature; // 创建一个新的要素 newFeature.setGeometry(combinedGeom); bool b = newFeature.hasGeometry(); if (b == true) { //newFeature.setId(m_currentlayer->featureCount()); //newFeature.setAttributes(att); m_currentlayer->dataProvider()->addFeature(newFeature); // 添加新的要素到图层 //m_currentlayer->commitChanges(); // 提交更改 } m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers();
}
4.15 线反向
功能点描述:
线反向:线的坐标序列反向。
运行截图:
操作前:
图 70 线反向示例数据
操作后:
图 71 线反向成功
主要代码:
代码派生了QgsMapTool类,在reverseGeometry函数负责反转传入的几何对象geom中的线要素,通过判断几何对象类型并处理线要素的坐标序列实现。而reverseLines函数则通过遍历当前图层中选中要素,调用reverseGeometry函数实现对每个选中要素线要素的反转,并将结果应用到地图显示,最后提交编辑并刷新地图画布和所有图层,以更新反转后的要素在地图上的展示。
void QgsMapToolReverseLines::reverseGeometry(QgsGeometry& geom) { if (geom.type() == QgsWkbTypes::LineString || geom.type() == QgsWkbTypes::MultiLineString) {//用于检查几何对象的类型是否为线要素。 QgsAbstractGeometry* newGeom = nullptr;//声明一个指向抽象几何对象的指针,并初始化为nullptr。 if (geom.type() == QgsWkbTypes::LineString) { QgsLineString* line = new QgsLineString(geom.asPolyline());//创建一个线要素对象,并使用geom.asPolyline()将几何对象转换为线要素的坐标序列。 if (reversePointsOrder) { std::reverse(reversePoints.begin(), reversePoints.end()); } line->reversed();//反转线要素的顺序。 newGeom = line; } if (newGeom) { geom = QgsGeometry(newGeom); delete newGeom; } }
} void QgsMapToolReverseLines::reverseLines() { QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { QgsGeometry geom = selectedFeature.geometry(); reverseGeometry(geom); if (!geom.isNull()) { m_currentlayer->startEditing(); QgsFeatureId id = selectedFeature.id(); m_currentlayer->changeGeometry(id, geom); } } reversePointsOrder = false; reversePoints.clear(); m_currentlayer->commitChanges(); m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers();
}
4.16 区弧段转线
用户使用区弧段转线时,需要先使用选择要素或按区域选择要素的功能选择一个或多个面要素,再点击转换按钮,选择的面要素如下图中黄色高亮选中区域所示:
图 72 选择面要素
转换后线要素显示在了一个新的图层中,如下图所示:
图 73 转换成功
面弧段转线功能的实现代码如下,通过迭代遍历选中的多边形要素,在内存中创建一个新的矢量图层LinesLayer。对于每个选中的多边形要素,获取其几何体并将其转换为线几何体类型,然后创建一个新的线要素lineFeature,将转换后的线几何体设置为其几何体。最后,将新的线要素添加到LinesLayer的数据提供者中。该图层被添加到当前QGIS项目中,并刷新地图视图,以便显示新添加的线要素。
void QgsMapToolPolygonsToLines::PolygonsToLines()
{ QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; QgsFeatureList newfeatures; while (selectedFeatureIt.nextFeature(selectedFeature)) { QgsGeometry geom = selectedFeature.geometry(); if (geom.type() == QgsWkbTypes::PolygonGeometry) { QgsGeometry lineGeom = geom.convertToType(QgsWkbTypes::LineGeometry, true); QgsFeature lineFeature; lineFeature.setGeometry(lineGeom); bool b = lineFeature.hasGeometry(); newfeatures.append(lineFeature); } } QgsVectorLayer* LinesLayer = new QgsVectorLayer("LineString?crs=epsg:4326", "PolygonsToLines", "memory"); LinesLayer->startEditing(); LinesLayer->dataProvider()->addFeatures(newfeatures); //LinesLayer->commitChanges(); QgsProject::instance()->addMapLayer(LinesLayer); m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers();
}