饮用水品牌营销型网站,新航道培训机构怎么样,嘉兴公司的网站设计,微信制作软件实习二 QGIS插件开发
2.1 任务要求
a#xff09;用C语言编写qgis插件#xff0c;实现带有x/y坐标的文本文件的地图显示。 用文件流fstream操作文本文件#xff0c;读取其中的坐标数据。基于QgsPlugin相关类派生出一个插件#xff0c;并加到插件工厂中。基于QgsVectorLaye…实习二 QGIS插件开发
2.1 任务要求
a用C语言编写qgis插件实现带有x/y坐标的文本文件的地图显示。 用文件流fstream操作文本文件读取其中的坐标数据。基于QgsPlugin相关类派生出一个插件并加到插件工厂中。基于QgsVectorLayer创建一个点矢量图层vecLayer将坐标数据形成要素添加vecLayer中并将vecLayer添加到mapCavas中进行显示。 b用python语言编写qgis插件实现中文地名的解析与地图显示。 用文件流fstream操作文本文件读取其中的地名文本数据。基于python新建一个qgis功能插件。利用腾讯地图服务api解析地名形成坐标并将坐标形成要素添加到点图层中进行显示。 2.2 完成过程
2.2.1 C插件
QGIS的C插件开发主要是在QGIS软件中新增一个插件实现将txt文本中带有x/y坐标的文本文件转化为shp文件加载到QGIS窗口中。具体操作步骤如下
在确定需要进行 C插件式开发的时候结合 QGIS 的插件的特点这是一种利用dll库进行开发的方式。具体来说需要在 QGIS 进行以下的设置也即修改工程的属性将输出的文件的方式更改为以.dll 作为后缀名的文件。如图2.2.1-1所示 图2.2.1-1 修改配置类型
这里的设计插件采用的空的QT Widget 窗口设计的窗口的显示内容如图中所示 这里的窗口设置不同通过Qt Creator创建的而是通过在代码中重新生成一个Qt Widget 来生成的同时将其可视化出来具体的插件的界面显示内容如图2.2.1-2所示。 图2.2.1-2 插件样式
在设计完成插件的界面显示内容之后可以对原来的代码进行重新编译的操作。首先需要引入一些需要引用到的头文件如图2.2.1-3所示。 图2.2.1-3 引用头文件
读取输入的文件中的坐标信息并将读取到的点坐标存储起来。采用的容器是QVector这种动态数组其中点的坐标对类型为QPointF。读取文件时候打开的方式采用的是以只读的方式以及文本形式打开文件。在可以成功打开文件之后采用文件流的方式读取文件中的数据。
文件中存储的坐标每一行都是以类似于“12,23”这种方式进行存储的因此每一行选用“,”作为分割符从而可以成功获取每一个点的坐标信息。代码如图2.2.1-4所示。 图2.2.1-4 坐标读取文件
在C中创建点图层的方式与Python中是十分类似的不同之处在于在Python中使用GDAL库创建shp文件的时候需要显式地进行驱动的注册。但是在C中可以直接通过调用QGIS的接口来隐去部分操作。
具体来说在C中首先是注册了一个点矢量图层之后在点矢量图层中创建了一系列的字段并提交了更改。之后根据上面已经得到的文件中的各个点的坐标数组对其进行遍历。创建相应的feature为每一个要素设置对应的字段信息以及相应的几何信息、属性信息。之后将每一个要素的信息添加到图层组的要素信息组中。最后调用QgsVectorFileWriter()函数实现shp文件的创建。主要需要传入的参数包括图层信息输出文件名编码格式坐标系统信息以及矢量文件的格式信息等。至此一个点矢量图层即已被成功创建。代码如图2.2.1-5所示。 图2.2.1-5 shp文件创建
点击上方的运行即可在对应项目文件下的Release文件夹中输出封装好插件功能的动态链接库“ceshi.dll”文件。如图2.2.1-6所示。 图2.2.1-6 编译测试插件
将编译输出得到的dll文件复制到QGIS安装文件夹的【apps】-【qgis-ltr】-【plugins】文件夹中操作如图2.2.2-7所示。 图2.2.1-7 安装插件
至此可以在 QGIS 中的菜单栏中看到加载的插件如下图中的红框中所显示的内容插件的名称叫做test点击按钮。至此插件已经成功加载到对应的窗口位置中。在插件窗口中输入文件地址为“坐标.txt”如图2.2.1-8所示。 图2.2.1-8 坐标文本文件
点击save 按钮弹出选择存储文件结果的框最终得到的shp文件的显示效果如图2.2.1-9所示。 图2.2.1-9 显示结果
2.2.2 Python插件
QGIS的python插件开发主要是在QGIS软件中新增一个插件geocode tool bar实现将txt文本中地址数据通过调用腾讯地理编码服务解析成经纬度并写入到shp文件加载到QGIS窗口中。具体操作步骤如下
首先双击QGIS Deskopt启动QGIS软件进入到主界面依次在工具栏点击【插件】-【管理并安装插件】-【新插件】搜索并安装“Plugin Builder”和“Plugin Reloader”。操作如图2.2.2-1所示。 图2.2.2-1 下载并安装插件
下载完两个插件后可以在【已安装】中检查两个插件是否都安装配置好如图2.2.2-2所示。 图2.2.2-2 检查插件情况
Tips这里安装的两个插件其中Plugin Builder是用来生成QGIS插件Python工程模板工具而Plugin Reloader则是用来在QGIS中重新加载插件对插件进行调试的工具。
由于本处应用的是Python进行相关的配置开发故这里需要检查一下对应的的QGIS的Python版本。打开QGIS点击Python 控制台图标打开QGIS中的Python 控制台。输入“QStandardPaths.standardLocations(QStandardPaths.AppDataLocation)”运行后即可得到相应的QGIS内置的Python安装位置操作如图2.2.2-3所示。 图2.2.2-3 检查Python版本
接下来打开PyCharm依次点击【File】-【Settings】-【project:工程名】-【Python Interpreter】-【Add】选择【Virtualenv Environment】中的“Existing environment”“Interpreter”选择QGIS 3.34.10\bin下的“python-qgis-ltr.bat”文件这个批处理文件把QGIS的Python环境都配置好了只要把它设置为解释器就不需要再配置别的环境变量了。然后点击ok即可。操作如图2.2.2-4所示。 图2.2.2-4 配置Python解释器
配置完成后不难发现在QGIS的预设中就已经提供了GDAL和shapely、PyQt5等二次开发库。如图2.2.2-5所示。 图2.2.2-5 检查Python解释器
配置完成python的环境后回到QGIS在工具栏重要依次点击【插件】-【Plugin Builder】-【Plugin Builder】。然后填写好插件信息点击next。操作如图2.2.2-6所示。 图2.2.2-6 填写插件信息
接下来填写插件说明这里内容可以不必细纠填写完点击next。操作如图2.2.2-7所示。 图2.2.2-7 插件说明
接下来在【Template】选择“Tool button with dialog”即带工具按钮的对话框。并填写【Text for the menu item】为“”将【Menu】选择为“Plugins”接下来这个插件会在工具的【Plugins】目录下。然后点击next。操作如图2.2.2-8所示。 图2.2.2-8 插件选择
这里的国际化、帮助、单元测试、帮助脚本等都默认勾选点击next。操作如图2.2.2-9所示。 图2.2.2-9 插件勾选
接下来勾选“Flag the plugin as experimental”标识为测试插件别的保持默认就行点击next。操作如图2.2.2-10所示。 图2.2.2-10 测试插件
最后是为创建的工程选择一个路径路径是一个文件夹点击“Generate”。操作如图2.2.2-11所示。 图2.2.2-11 创建插件位置
接下来会弹出创建结果即“You just built a plugin for QGIS”如图2.2.2-12所示。 图2.2.2-12 创建结果
完成新建插件的设置后即可得到相应的项目文件夹由于系统版本原因这里已经预编译好了项目快捷文件“compile.bat”并且也出现了对应的插件主体功能文件。如图2.2.2-13所示。 图2.2.2-13 项目文件夹
当然该文件并没有成功的加入到QGIS的插件中这里还需要将该文件夹复制到“C:\Users\33439\AppData\Roaming\QGIS\QGIS3\profiles\default\python\plugins”中再重启QGIS依次打开工具栏的【插件】-【管理并安装插件】-【已安装】才能看到新添加的插件。勾选上该插件即会发现该插件的工具图标也在界面上如图2.2.2-14所示。 图2.2.2-14 新增插件
点击该插件即可得到插件原始的预设样式如图2.2.2-15所示。 图2.2.2-15 插件初始打开样式
进行完如上操作插件的雏形已经有了接下来要在这个雏形的基础上开发。在开发之前首先需要把界面重新设置一下。这里可以直接打开文件夹选择geocode_tool文件夹下的geocode_tool_dialog_base.ui文件使用QT Creator打开在主界面选择【界面编辑器】点击ok。操作如图2.2.2-16所示。 图2.2.2-16 打开ui文件
接下来在左侧控件栏中选择对应的控件拖拽到界面上在右侧属性框中修改控件的objectName。新增加的文本框和按钮的名称分别是lineEditTxtlineEditShplineEditKeypushButtonTxtpushButtonShp。操作最终得到的ui如图2.2.2-17所示。 图2.2.2-17 修改后的ui
接下来可以回到QGIS软件中检查一下UI是否已经成功被修改。在工具栏中依次打开【插件】-【Plugin Reloader】-【Configure】打开【Configure Plugin Reloader】对话框。在【Select the plugin you want to reload】中选择“Geocode Tool“点击ok。操作如图2.2.2-18所示。 图2.2.2-18 更新插件
更新完插件后再点击一下插件即可发现插件的UI已经成功更改如图2.2.2-19所示。 图2.2.2-19 更新ui后的插件
显然这里界面的ui只是一个壳子没有槽函数响应信号因此需要对功能函数进行编码这里打开文件夹中的“geocode_tool.py”文件进行修改。
首先需要引入QT库函数如图2.2.2-20所示。 图2.2.2-20 引入库函数
接下来新增两个方法select_input_file和select_output_file用以读取txt文件和shp文件的路径。如图2.2.2-21所示。 图2.2.2-21 新增读写文件路径
完成之后在run方法中添加两行代码调用select_input_file和select_output_file方法点击pushButtonTxt和pushButtonShp按钮的时候触发。如图2.2.2-22所示。 图2.2.2-22 修改run函数添加按钮触发
接下来新增两个个方法其中readTxt函数应用于读写txt文件writeShp函数应用于读写shp文件。如图2.2.2-23所示。 图2.2.2-23 新增读写txt和shp函数
最后新增一个方法geoCode,用于调用腾讯地图API密钥如图2.2.2-24所示。 图2.2.2-24 新增getcode函数
在run方法的if result后面添加需要执行的代码。如图2.2.2-25所示。 图2.2.-25 显示结果
其中完整的源代码将在2.4节中展示。此处不在赘述。
对代码编译完成后将所有的文件保存将插件再次更新再次点击插件将对应的txt文件和腾讯地图API密钥输入选择好输出的shp文件地址点击“确定”即可得到相应的解析地址。操作如图2.2.2-26所示。 图2.2.2-26 使用插件
在界面上可以看到出现的插件情况和相应的点已被成功解析。如图2.2.2-27所示。 图2.2.2-27 解析情况
同样可以右键图层打开【属性】将其【源】中的【数据编码】设置为“UTF-8”如图2.2.2-28所示。 图2.2.2-28 修改数据源
接下来点击OK将图层属性表打开即可看到对应的点的属性信息打开对应的txt文件发现相符合如图2.2.2-29所示。 图2.2.2-29 属性表对应txt文件
2.3 结果展示
2.3.1 C插件结果
QGIS的C插件开发主要是在QGIS软件中新增一个插件实现将txt文本中带有x/y坐标的文本文件转化为shp文件加载到QGIS窗口中。插件如图2.3.1-1所示。 图2.3.1-1 插件样式
当我们使用插件打开测试数据并进行转换时最终可以正确的解析得到两个点坐标的矢量文件如图2.3.1-2所示。 图2.3.1-2 C插件使用结果
2.3.2 Python插件结果
QGIS的python插件开发主要是在QGIS软件中新增一个插件geocode tool bar实现将txt文本中地址数据通过调用腾讯地理编码服务解析成经纬度并写入到shp文件加载到QGIS窗口中。插件如图2.3.2-1所示。 图2.3.2-1 插件样式
当我们使用插件打开测试数据并进行转换时最终可以正确的解析得到三个点坐标的矢量文件如图2.3.2-2所示。 图2.3.2-2 Python插件结果
2.4 关键代码
2.4.1 C插件代码
本处的关键代码为在Visual Studio中配置的文件“ceshi.cpp”中写入的应用QGIS二次开发实现坐标解析转换的源代码
#include ceshi.h
#include qfiledialog.h
#include qapplication.h
#include qgsproject.h
#include QgsVectorLayer.h
#include QgsVectorFileWriter.h
#include QString.h
ceshi::ceshi(QWidget *parent): QMainWindow(parent)
{ui.setupUi(this);
}
ceshi::~ceshi()
{}
void ceshi::on_actionOpenProject_triggered()
{QString filename QFileDialog::getOpenFileName(this, QStringLiteral(选择工程文件), , QGIS project (*.qgs));QFileInfo fi(filename);if (!fi.exists()){return;}QgsProject::instance()-read(filename);m_curMapLayer QgsProject::instance()-mapLayer(0);
}
QVectorQPointF getcoordinates(QString myfile) {QVectorQPointF coordinates;QFile file(myfile);if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {QTextStream in(file);while (!in.atEnd()) {QString line in.readLine();QStringList parts line.split(,);if (parts.size() 2) {bool okX, okY;double x parts[0].toDouble(okX);double y parts[1].toDouble(okY);if (okX okY) {coordinates.append(QPointF(x, y));}}}file.close();return coordinates;}else {// 如果文件无法打开可以在这里打印错误信息或处理异常qWarning() Could not open file: myfile;return coordinates; // 返回空的 QVector}
}
void ceshi::outputButton_clicked() {// 获取输入文件路径QString myfile inputFilename-text();QVectorQPointF coordinates getcoordinates(myfile);// 创建一个新的点矢量图层QgsVectorLayer* layer new QgsVectorLayer(Point?crsEPSG:4326, pointLayer, memory);if (!layer-isValid()) {qWarning() Layer creation failed!;return;}// 添加属性字段QgsField idField(id, QVariant::Int);QgsFields fields;fields.append(idField);layer-dataProvider()-addAttributes(fields.toList());layer-startEditing();// 添加字段到图层layer-addAttribute(idField);layer-commitChanges();layer-updateFields();// 将点数据添加到图层中int id 1;QgsFeatureList features;QString myId id;for (const QPointF point : coordinates) {QgsFeature feature;feature.setFields(layer-fields());feature.setAttribute(myId, id);feature.setGeometry(QgsGeometry::fromPointXY(QgsPointXY(point.x(), point.y())));features.append(feature);}// 添加特征到图层layer-dataProvider()-addFeatures(features);layer-updateExtents(); // 更新图层范围// 保存图层为 ShapefileQString outputFilename QFileDialog::getSaveFileName(window, tr(Save File), , tr(Shapefiles (*.shp)));if (!outputFilename.isEmpty()) {if (!outputFilename.endsWith(.shp, Qt::CaseInsensitive)) {outputFilename .shp;}QgsVectorFileWriter::writeAsVectorFormat(layer, outputFilename, UTF-8, layer-crs(), ESRI Shapefile);}
}2.4.2 Python插件代码
本处的关键代码为在Pycharm中打开的文件“geocode_tool.py”中写入的应用QGIS二次开发实现坐标解析转换的源代码
# -*- coding: utf-8 -*-/***************************************************************************GeocodeToolA QGIS plugin地理编码工具Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/-------------------begin : 2022-10-11git sha : $Format:%H$copyright : (C) 2022 by ttemail : 1404167294qq.com***************************************************************************/
/**************************************************************************** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ****************************************************************************/from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QFileDialog
from qgis.core import Qgis, QgsProject, QgsVectorLayer
from .geocode_tool_dialog import GeocodeToolDialog
import os.path
import requests
try:from osgeo import gdalfrom osgeo import ogrfrom osgeo import osr
except ImportError:import gdalimport ogrimport osrclass GeocodeTool:QGIS Plugin Implementation.def __init__(self, iface):Constructor.:param iface: An interface instance that will be passed to this classwhich provides the hook by which you can manipulate the QGISapplication at run time.:type iface: QgsInterface# Save reference to the QGIS interfaceself.iface iface# initialize plugin directoryself.plugin_dir os.path.dirname(__file__)# initialize localelocale QSettings().value(locale/userLocale)[0:2]locale_path os.path.join(self.plugin_dir,i18n,GeocodeTool_{}.qm.format(locale))if os.path.exists(locale_path):self.translator QTranslator()self.translator.load(locale_path)QCoreApplication.installTranslator(self.translator)# Declare instance attributesself.actions []self.menu self.tr(uGeocode Tool)# Check if plugin was started the first time in current QGIS session# Must be set in initGui() to survive plugin reloadsself.first_start None# noinspection PyMethodMayBeStaticdef tr(self, message):Get the translation for a string using Qt translation API.We implement this ourselves since we do not inherit QObject.:param message: String for translation.:type message: str, QString:returns: Translated version of message.:rtype: QString# noinspection PyTypeChecker,PyArgumentList,PyCallByClassreturn QCoreApplication.translate(GeocodeTool, message)def add_action(self,icon_path,text,callback,enabled_flagTrue,add_to_menuTrue,add_to_toolbarTrue,status_tipNone,whats_thisNone,parentNone):Add a toolbar icon to the toolbar.:param icon_path: Path to the icon for this action. Can be a resourcepath (e.g. :/plugins/foo/bar.png) or a normal file system path.:type icon_path: str:param text: Text that should be shown in menu items for this action.:type text: str:param callback: Function to be called when the action is triggered.:type callback: function:param enabled_flag: A flag indicating if the action should be enabledby default. Defaults to True.:type enabled_flag: bool:param add_to_menu: Flag indicating whether the action should alsobe added to the menu. Defaults to True.:type add_to_menu: bool:param add_to_toolbar: Flag indicating whether the action should alsobe added to the toolbar. Defaults to True.:type add_to_toolbar: bool:param status_tip: Optional text to show in a popup when mouse pointerhovers over the action.:type status_tip: str:param parent: Parent widget for the new action. Defaults None.:type parent: QWidget:param whats_this: Optional text to show in the status bar when themouse pointer hovers over the action.:returns: The action that was created. Note that the action is alsoadded to self.actions list.:rtype: QActionicon QIcon(icon_path)action QAction(icon, text, parent)action.triggered.connect(callback)action.setEnabled(enabled_flag)if status_tip is not None:action.setStatusTip(status_tip)if whats_this is not None:action.setWhatsThis(whats_this)if add_to_toolbar:# Adds plugin icon to Plugins toolbarself.iface.addToolBarIcon(action)if add_to_menu:self.iface.addPluginToMenu(self.menu,action)self.actions.append(action)return actiondef initGui(self):Create the menu entries and toolbar icons inside the QGIS GUI.icon_path :/plugins/geocode_tool/icon.pngself.add_action(icon_path,textself.tr(ugeocode tool bar),callbackself.run,parentself.iface.mainWindow())# will be set False in run()self.first_start Truedef unload(self):Removes the plugin menu item and icon from QGIS GUI.for action in self.actions:self.iface.removePluginMenu(self.tr(uGeocode Tool),action)self.iface.removeToolBarIcon(action)# 读txt文件def readTxt(self, path_str):f open(path_str, r, encodingutf-8)flines f.readlines()address_list []for l in flines:address_list.append(l.strip(\n))f.close()return address_list# 写shp文件def writeShp(self, path_str, geocode_list):# 支持中文路径gdal.SetConfigOption(GDAL_FILENAME_IS_UTF8, YES)# 属性表字段支持中文gdal.SetConfigOption(SHAPE_ENCODING, UTF-8)# 注册驱动ogr.RegisterAll()# 创建shp数据strDriverName ESRI ShapefileoDriver ogr.GetDriverByName(strDriverName)if oDriver None:return 驱动不可用 strDriverName# 创建数据源oDS oDriver.CreateDataSource(path_str)if oDS None:return 创建文件失败 path_str# 创建一个多边形图层指定坐标系为WGS84papszLCO []geosrs osr.SpatialReference()geosrs.SetWellKnownGeogCS(WGS84)# 线ogr_type ogr.wkbLineString# 点ogr_type ogr.wkbPointogr_type ogr.wkbPoint# 面的类型为Polygon线的类型为Polyline点的类型为PointoLayer oDS.CreateLayer(Point, geosrs, ogr_type, papszLCO)if oLayer None:return 图层创建失败# 创建属性表# 创建id字段oId ogr.FieldDefn(id, ogr.OFTInteger)oLayer.CreateField(oId, 1)# 创建address、title、level字段oAddress ogr.FieldDefn(address, ogr.OFTString)oLayer.CreateField(oAddress, 1)oTitle ogr.FieldDefn(title, ogr.OFTString)oLayer.CreateField(oTitle, 1)oLevel ogr.FieldDefn(level, ogr.OFTInteger)oLayer.CreateField(oLevel, 1)oDefn oLayer.GetLayerDefn()# 创建要素# 数据集for index, f in enumerate(geocode_list):oFeaturePolygon ogr.Feature(oDefn)oFeaturePolygon.SetField(id, index)oFeaturePolygon.SetField(address, f[address])oFeaturePolygon.SetField(title, f[title])oFeaturePolygon.SetField(level, f[level])geomPolygon ogr.CreateGeometryFromWkt(f[point])oFeaturePolygon.SetGeometry(geomPolygon)oLayer.CreateFeature(oFeaturePolygon)# 创建完成后关闭进程oDS.Destroy()return 数据集创建完成def run(self):Run method that performs all the real work# Create the dialog with elements (after translation) and keep reference# Only create GUI ONCE in callback, so that it will only load when the plugin is startedif self.first_start True:self.first_start Falseself.dlg GeocodeToolDialog()# 点击按钮确定路径self.dlg.pushButtonTxt.clicked.connect(self.select_input_file)self.dlg.pushButtonShp.clicked.connect(self.select_output_file)# show the dialogself.dlg.show()# Run the dialog event loopresult self.dlg.exec_()# See if OK was pressedif result:# 获取txt文件路径shp文件路径腾讯keytxtname self.dlg.lineEditTxt.text()shpname self.dlg.lineEditShp.text()tencentkey self.dlg.lineEditKey.text()address_list self.readTxt(txtname)# 读txt文件中的内容并将geocode结果写入shpgeocode_list []for address in address_list:result self.geoCode(address, tencentkey)if result ! None:geocode_list.append(result)self.writeShp(shpname, geocode_list)# 将结果加载QGIS界面layerName shpname.split(/)[len(shpname.split(/)) - 1].replace(.shp, )vlayer QgsVectorLayer(shpname, layerName, ogr)if vlayer.isValid():QgsProject.instance().addMapLayer(vlayer)else:print(图层加载失败)pass# 在QGIS界面上打印结果self.iface.messageBar().pushMessage(成功, 加载图层 layerName, levelQgis.Success, duration3)
# 加载txt文件的路径def select_input_file(self):filename,_filter QFileDialog.getOpenFileName(self.dlg,Select input file,,*.txt)self.dlg.lineEditTxt.setText(filename)# 导出shp文件的路径def select_output_file(self):filename,_filter QFileDialog.getSaveFileName(self.dlg,Select output file,,*.shp)self.dlg.lineEditShp.setText(filename)
# 调用腾讯geocode服务def geoCode(self, address, key):url https://apis.map.qq.com/ws/geocoder/v1/?address address key keyreponse requests.get(urlurl)reponse.encoding utf-8data reponse.json()try:if data[status] 0:return {address: address, title: data[result][title],point: POINT( str(data[result][location][lng]) str(data[result][location][lat]) ),level: data[result][level]}except BaseException as e:print(e)