【QGIS二次开发】地图编辑-06
系列目录:
【QGIS二次开发】地图显示与交互-01_qgis二次开发加载地图案例-CSDN博客
【QGIS二次开发】地图显示与交互-02_setlayerlabeling-CSDN博客
【QGIS二次开发】地图符号与色表-03-CSDN博客
【QGIS二次开发】地图编辑-04-CSDN博客
【QGIS二次开发】地图编辑-05-CSDN博客
4.8 光滑要素
用户进行光滑要素时选择对应图层,打开编辑模式,点击选择/区域选择按钮在屏幕中选中一个或多个线要素后点击线要素光滑按钮,弹出线要素光滑对话框,对话框为用户提供了多种算法用于选择,以及平滑程度的滑动条能够调节平滑等级,用户点击确定后程序进行平滑计算,最终平滑的效果中红色线标识了线要素平滑前的几何位置,黄线为平滑后的几何位置,操作流程和平滑效果如下图所示:
图 57 线光滑面板
图 58 线光滑成功
线平滑的实现如下,通过在派生的地图工具类QgsMapToolSmoothLines中添加成员函数smoothLine对当前图层中选中的线要素进行平滑处理。通过遍历选中要素,根据指定的平滑算法"Chaikin Smooth"或"Simplify"对几何体进行处理,并在处理前后通过可视化的方式进行突出显示,方便查看平滑效果。代码在平滑处理之前启动图层编辑,并在处理完成后重新添加图层到地图项目中,以确保更新后的几何体在地图中正确显示。值得注意的是,代码中的编辑提交部分被注释,可能是因为在循环中处理每个要素时不应立即提交,而是在整个循环完成后一次性提交。
void QgsMapToolSmoothLines::smoothLine() // 光滑线
{ if (m_currentlayer) { QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { if (algorithm == "Chaikin Smooth") { m_currentlayer->startEditing(); // 图层开始编辑 QgsHighlight* hightlight = new QgsHighlight(m_mapCanvas, selectedFeature, m_currentlayer); hightlight->setColor(QColor("red")); QgsGeometry geometry = selectedFeature.geometry(); // 获取feature的几何体 QgsFeatureId id = selectedFeature.id(); QgsGeometry smoothedGeometry = geometry.smooth(iterations, 0.5, -1.0, 180.0); // 使用给定的参数对几何体进行平滑 selectedFeature.setGeometry(smoothedGeometry); // 将平滑后的几何体保存回feature m_currentlayer->changeGeometry(id, smoothedGeometry); } else if (algorithm == "Simplify") { m_currentlayer->startEditing(); // 图层开始编辑 QgsHighlight* hightlight = new QgsHighlight(m_mapCanvas, selectedFeature, m_currentlayer); hightlight->setColor(QColor("blue")); QgsGeometry geometry = selectedFeature.geometry(); // 获取feature的几何体 QgsFeatureId id = selectedFeature.id(); QgsGeometry simplifyGeometry = geometry.simplify(iterations); // 使用给定的参数对几何体进行平滑 selectedFeature.setGeometry(simplifyGeometry); // 将平滑后的几何体保存回feature m_currentlayer->changeGeometry(id, simplifyGeometry); } } //m_currentlayer->commitChanges(); QgsProject::instance()->addMapLayer(m_currentlayer); m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers(); }
}
4.9 计算垂线
用户使用计算垂线功能时,需要先在线图层中选择一个线要素,当用户选择多个要素或者没有选择要素时会弹出报错框进行提醒,接着用户在界面中点击一个点,系统会自动计算点击方位与选中线要素的垂线并输出为一个新的图层,实现的效果如下图所示:
图 59 计算垂线
代码实现如下,通过在派生的地图工具类QgsMapToolVertical中添加成员函canvasPressEvent。该函数响应鼠标左键点击事件,通过获取点击位置的坐标,在当前图层中选中的要素上创建垂直线段。具体实现步骤包括将点击位置转换为地图坐标,创建一个新的内存图层QgsVectorLayer用于存储垂直线段,遍历当前图层中的选中要素,计算每个要素上离点击位置最近的垂足,并以此信息创建新的线段要素,将这些新要素添加到内存图层中,最后将内存图层添加到地图项目中并刷新地图画布。
void QgsMapToolVertical::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)); QgsVectorLayer* verticalLayer = new QgsVectorLayer("LineString?crs=epsg:4326", "Vertical", "memory"); verticalLayer->startEditing(); QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { QgsGeometry geom = selectedFeature.geometry(); QgsGeometry foot = geom.nearestPoint(QgsGeometry::fromPointXY(mPoint)); // 垂足 QgsPointXY footpoint = foot.asPoint(); // 创建一个新的线段 QgsPolyline polyline; polyline.append(QgsPoint(mPoint)); polyline.append(QgsPoint(footpoint)); QgsFeature verticalFeature; verticalFeature.setGeometry(QgsGeometry::fromPolyline(polyline)); bool b = verticalFeature.hasGeometry(); // 将新的要素添加到数据提供者 verticalLayer->dataProvider()->addFeature(verticalFeature); QgsProject::instance()->addMapLayer(verticalLayer); m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers(); //pRubBand->reset(); //pRubBand->addPoint(mPoint); //pRubBand->addPoint(footpoint); } } //else if (e->button() == Qt::RightButton) { // pRubBand->reset(); //}
}
4.10 线上加点
用户利用工具进行线上加点操作时,需要仅选中一条线要素,通过鼠标在界面进行添加点的操作,系统会自动识别线要素中离鼠标点击位置最近的顶点,在顶点间插入鼠标点击的位置,用户未操作前的线要素几何为下图所示:
图 60 示例数据
在界面中点击添加两个点后,线要素添加点后的效果如下图所示:
图 61 线上加点成功
实现线上加点的代码如下,通过在派生的地图工具类QgsMapToolAddPoint中添加成员函数canvasPressEvent实现线上加点的功能。该函数响应鼠标左键点击事件,通过获取点击位置的坐标,在当前图层中选中的线要素上添加新的顶点。具体实现包括将点击位置转换为地图坐标,遍历当前图层中的选中要素,计算每个要素上离点击位置最近的顶点,然后在该顶点的位置插入新的顶点。随后,更新要素的几何体,并刷新地图画布以反映变化。
void QgsMapToolAddPoint::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)); // 线上加点 QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { // 只循环一次 QgsGeometry geom = selectedFeature.geometry(); int closestVertexIndex, previousVertexIndex, nextVertexIndex; double sqrDist; // 最近距离 QgsPointXY nearestPoint = geom.closestVertex(mPoint, closestVertexIndex, previousVertexIndex, nextVertexIndex, sqrDist); //QgsGeometry closestPoint = geom.nearestPoint(QgsGeometry::fromPointXY(mPoint)); // 计算新点到线段的最近点 //double vertexIndex = geom.lineLocatePoint(closestPoint); // 计算最近点在线段上的位置 geom.insertVertex(mPoint.x(), mPoint.y(), closestVertexIndex); // 在最近点的位置插入新的顶点 // 更新要素的几何体 m_currentlayer->startEditing(); selectedFeature.setGeometry(geom); m_currentlayer->updateFeature(selectedFeature); //m_currentlayer->commitChanges(); } } m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers();
}
4.11 线上移点
线上移点的操作与线上加点类似,先选中一条线要素后在地图中点击线要素的顶点进行鼠标拖动,进行线上顶点的移动,实现的效果如下图所示,操作基于上一步加点后的线要素继续进行操作:
图 62 线上移点
线上移点的实现代码如下,分别实现了鼠标左键按下和释放事件的处理。在鼠标左键按下时,该函数记录了起始坐标,遍历选中的线要素并计算最近顶点。而在鼠标左键释放时,获取了释放时的坐标,同样遍历选中的线要素,然后在最近顶点的位置移动顶点。最后,通过更新要素的几何体和刷新地图画布,实现了线要素上顶点的移动功。
void QgsMapToolMovePoint::canvasPressEvent(QgsMapMouseEvent* e)
{ if (e->button() == Qt::LeftButton) { double xc = e->x(); double yc = e->y(); // 得到当前坐标变换对象 const QgsMapToPixel* pTransform = m_mapCanvas->getCoordinateTransform(); // 转换成地图坐标并记录为起始坐标 startpoint.setX(pTransform->toMapCoordinates(xc, yc).x()); startpoint.setY(pTransform->toMapCoordinates(xc, yc).y()); QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { // 只循环一次 QgsGeometry geom = selectedFeature.geometry(); double sqrDist; // 最近距离 QgsPointXY nearestPoint = geom.closestVertex(startpoint, closestVertexIndex, previousVertexIndex, nextVertexIndex, sqrDist); } }
} void QgsMapToolMovePoint::canvasReleaseEvent(QgsMapMouseEvent* e)
{ if (e->button() == Qt::LeftButton) { double xc = e->x(); double yc = e->y(); // 得到当前坐标变换对象 const QgsMapToPixel* pTransform = m_mapCanvas->getCoordinateTransform(); // 转换成地图坐标 endpoint.setX(pTransform->toMapCoordinates(xc, yc).x()); endpoint.setY(pTransform->toMapCoordinates(xc, yc).y()); // 线上加点 QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { // 只循环一次 QgsGeometry geom = selectedFeature.geometry(); geom.moveVertex(endpoint.x(), endpoint.y(), closestVertexIndex); // 更新要素的几何体 m_currentlayer->startEditing(); selectedFeature.setGeometry(geom); m_currentlayer->updateFeature(selectedFeature); //m_currentlayer->commitChanges(); } m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers(); }
}
4.12 线上删点
用户进行线上删点的操作时,同样通过鼠标在地图中进行点击,系统会自动识别选中要素中最近的顶点进行删除,操作演示时基于上面的步骤继续进行了演示,实现的线上删除点的功能效果如下:
图 63 线上删点
线上删点功能的代码实现如下,通过在派生的QgsMapTool类中响应鼠标左键点击事件,通过获取点击位置的坐标,在当前图层中选中的线要素上删除最近的顶点。具体实现步骤包括将点击位置转换为地图坐标,遍历当前图层中的选中要素,计算每个要素上离点击位置最近的顶点,然后删除该顶点,并通过更新要素的几何体和刷新地图画布,实现了线要素上最近顶点的删除操作。
void QgsMapToolDeletePoint::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)); // 线上加点 QgsFeatureIterator selectedFeatureIt = m_currentlayer->getSelectedFeatures(); QgsFeature selectedFeature; while (selectedFeatureIt.nextFeature(selectedFeature)) { // 只循环一次 QgsGeometry geom = selectedFeature.geometry(); int closestVertexIndex, previousVertexIndex, nextVertexIndex; double sqrDist; // 最近距离 QgsPointXY nearestPoint = geom.closestVertex(mPoint, closestVertexIndex, previousVertexIndex, nextVertexIndex, sqrDist); if (closestVertexIndex != -1) { geom.deleteVertex(closestVertexIndex); // 更新要素的几何体 m_currentlayer->startEditing(); selectedFeature.setGeometry(geom); m_currentlayer->updateFeature(selectedFeature); //m_currentlayer->commitChanges(); } } m_mapCanvas->refresh(); m_mapCanvas->refreshAllLayers(); }
}