QGIS二次开发(插件开发)
实习二 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。新增加的文本框和按钮的名称分别是lineEditTxt,lineEditShp,lineEditKey,pushButtonTxt,pushButtonShp。操作最终得到的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);
}
QVector<QPointF> getcoordinates(QString myfile) {QVector<QPointF> 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();QVector<QPointF> coordinates = getcoordinates(myfile);// 创建一个新的点矢量图层QgsVectorLayer* layer = new QgsVectorLayer("Point?crs=EPSG: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 : 1404167294@qq.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(u'&Geocode 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_flag=True,add_to_menu=True,add_to_toolbar=True,status_tip=None,whats_this=None,parent=None):"""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: QAction"""icon = 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.png'self.add_action(icon_path,text=self.tr(u'geocode tool bar'),callback=self.run,parent=self.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(u'&Geocode Tool'),action)self.iface.removeToolBarIcon(action)# 读txt文件def readTxt(self, path_str):f = open(path_str, 'r', encoding='utf-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 Shapefile"oDriver = 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, level=Qgis.Success, duration=3)
# 加载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(url=url)reponse.encoding = 'utf-8'data = 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)
相关文章:

QGIS二次开发(插件开发)
实习二 QGIS插件开发 2.1 任务要求 a)用C语言编写qgis插件,实现带有x/y坐标的文本文件的地图显示。 用文件流fstream操作文本文件,读取其中的坐标数据。基于QgsPlugin相关类派生出一个插件,并加到插件工厂中。基于QgsVectorLaye…...
Web防火墙和下一代防火墙的区别
介绍 客户经常询问“当我已经拥有下一代防火墙(NGFW)时,为什么需要Web应用程序防火墙(WAF)?”。本博文的目的是解释两种解决方案之间的区别,重点关注Web应用程序防火墙可以提供的附加值。 什么…...
Linux:alias别名永久有效
一、背景 日常使用bash时候,有些常用的命令参数的组合命令太长,很难记,此时可以利用Linux提供的alias命令生成命令的别名(命令的隐射),但是我们会发现,当退出了终端后重新登录就失效了ÿ…...

【递归与回溯深度解析:经典题解精讲(中篇)】—— LeetCode
文章目录 组合目标和组合总和字母大小写全排序优美的排列N皇后 组合 思路:回溯算法 问题要求从 1 到 n 中选出 k 个数的所有组合。 使用回溯算法递归构造解。 每次递归时,记录当前的组合路径,当组合长度达到 k 时,将其加入结果集…...

01.HTTPS的实现原理-HTTPS的概念
01.HTTPS的实现原理-HTTPS的概念 简介1. HTTPS的概念和安全性2. HTTPS的实现原理3. HTTPS和HTTP的区别4. OSI七层协议模型5. SSL和TLS的区别 简介 该系列文章主要讲述了HTTPS协议与HTTP协议的区别,以及HTTPS如何实现安全传输。内容分为三部分:HTTPS的实…...

一文详解MacOS+CLion——构建libtorch机器学习开发环境
对于希望在本地环境中进行深度学习开发的开发者来说,配置合适的工具链是至关重要的一步。本文旨在帮助您在 macOS 操作系统上,利用 CLion IDE 和 PyTorch 的 C依赖库——libtorch,快速搭建起一个高效的开发环境。这里我们将一步步地讲解如何下…...
【LeetCode 面试经典150题】详细题解之哈希表篇
【LeetCode 面试经典150题】详细题解之哈希表篇 1 哈希表的基础1.1 基础概念及实现1.2.1 哈希表的工作原理1.2.2 705.设计哈希集合1.2.3 706.设计哈希映射 1.2 HashMap相关1.2.1 基本操作1.2.2 遍历 1.3 Hashtable1.4 LinkedHashMap1.5 HashSet**1.5.1基本特性**1.5.2 基本方法…...

linux socket编程之udp_dict_serve服务端--引入配置文件
注意:本篇博客只是对上一篇博客功能的增加 1.创建配置文件(翻译) Dict.txt apple: 苹果 banana: 香蕉 cat: 猫 dog: 狗 book: 书 pen: 笔 happy: 快乐的 sad: 悲伤的 run: 跑 jump: 跳 teacher: 老师 student: 学生 car: 汽车 bus: 公交车 love: 爱 hate: 恨 hell…...
selenium学习笔记(二)
文章目录 前言设计模式POMPOM概念POM优势POM设计原则POM的实现 selenium的常用操作处理动态元素截图操作勾选复选框多层框架/窗口定位操作下拉框上传文件操作处理弹窗切换窗口拖拽操作 如何处理浏览器驱动更新导致的问题selenium与网站监控监听网页内容变化监控网络请求 seleni…...

宏集eX710物联网工控屏在石油开采机械中的应用与优势
案例概况 客户:天津某石油机械公司 应用产品:宏集eX710物联网工控屏 应用场景:钻井平台设备控制系统 一、应用背景 石油开采和生产过程复杂,涵盖钻井平台、采油设备、压缩机、分离器、管道输送系统等多种机械设备。这些设备通…...
linux——vi命令常用操作
一、vi模式 vi一般分为三种模式,分别是命令行模式、插入模式、末行模式 1.命令模式:控制屏幕光标的移动,按 :进入末行模式,按 i(其他插入命令也可) 进入插入模式; 2.插入模式&…...

vscode添加全局宏定义
利用vscode编辑代码时,设置了禁用非活动区域着色后,在一些编译脚本中配置的宏又识别不了 遇到#ifdef包住的代码就会变暗色,想查看代码不是很方便。如下图: 一 解决: 在vscode中添加全局宏定义。 二 步骤:…...
重装荣耀X14笔记本电脑踩坑记
这几天趁着有国补搞了台荣耀 X14笔记本电脑。到手后第一件事情对我来说当然是要重装成Windows 11 LTSC版。所以按以往的经验做了个USB启动安装盘,但发现上电后按F12能进入启动设备选择,可是USB分类下没有任何设备。重启按F2进入设置界面,关闭…...
Android `android.graphics.drawable` 包深度解析:架构与设计模式
Android android.graphics.drawable 包深度解析:架构与设计模式 目录 引言Drawable 概述Drawable 的架构 Drawable 类层次结构Drawable 的核心方法Drawable 的设计模式 装饰者模式工厂模式状态模式常用 Drawable 子类解析 BitmapDrawableShapeDrawableLayerDrawableStateList…...
Kotlin语言的软件工程
Kotlin语言的软件工程 引言 在现代软件开发中,选择合适的编程语言是项目成功的关键之一。随着移动互联网的迅猛发展,以及大数据和人工智能等新兴技术的崛起,Kotlin语言凭借其简洁、高效和安全等特性,迅速崛起为备受欢迎的编程语…...
面试经典 150 题——数组/字符串(一)
文章目录 1、合并两个有序数组1.1 题目链接1.2 题目描述1.3 解题代码1.4 解题思路 2、移除元素2.1 题目链接2.2 题目描述2.3 解题代码2.4 解题思路 3、删除有序数组中的重复项3.1 题目链接3.2 题目描述3.3 解题代码3.4 解题思路 4、删除有序数组中的重复项 II4.1 题目链接4.2 题…...

使用亚马逊针对 PyTorch 和 MinIO 的 S3 连接器实现可迭代式数据集
2023 年 11 月,Amazon 宣布推出适用于 PyTorch 的 S3 连接器。适用于 PyTorch 的 Amazon S3 连接器提供了专为 S3 对象存储构建的 PyTorch 数据集基元(数据集和数据加载器)的实现。它支持用于随机数据访问模式的地图样式数据集和用于流式处理…...

TestMAX/DFT Compiler:时序单元的类型、连接顺序和后DFT优化
相关阅读 TestMAX/DFT Compilerhttps://blog.csdn.net/weixin_45791458/category_12865937.html?spm1001.2014.3001.5482 时序单元的状态 未映射的时序单元(Unmapped Sequential Cell) 在Design Compiler读取了一个RTL设计后,Design Compiler内置的HDL Compiler工…...

CAN201 Introduction to Networking(计算机网络)Pt.3 网络层
文章目录 4.Network Layter(网络层)4.1 Overview4.2 Router(路由器)4.3 Internet Protocol4.4 IPv4 addressing4.5 NAT(network address translation,网路地址转换)4.6 IPv64.7 Generalized For…...

App Factory:简化和加速私人应用开发
App Factory是Codigger提供的一套先进的开发工具、库和API,旨在帮助开发人员在现有的软件基础上添加特定的功能或扩展。它为私人应用的创建、开发和发布提供了一整套先进的工具集、集成的相关资源库以及强大的API接口,使开发者能够在现有的Codigger架构之…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

练习(含atoi的模拟实现,自定义类型等练习)
一、结构体大小的计算及位段 (结构体大小计算及位段 详解请看:自定义类型:结构体进阶-CSDN博客) 1.在32位系统环境,编译选项为4字节对齐,那么sizeof(A)和sizeof(B)是多少? #pragma pack(4)st…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战
在现代战争中,电磁频谱已成为继陆、海、空、天之后的 “第五维战场”,雷达作为电磁频谱领域的关键装备,其干扰与抗干扰能力的较量,直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器,凭借数字射…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...