当前位置: 首页 > news >正文

【QGIS进阶】- 字段计算器Python函数实战:从数据清洗到自动化筛选

1. 为什么需要字段计算器的Python函数?

很多刚接触QGIS的朋友在处理属性表时,经常会遇到这样的困扰:数据源不规范,字段里混杂着各种特殊字符。比如我最近接手的一个市政管网项目,管径字段里就充斥着"X"、"+"、"Y2"这样的标记符号。这些数据如果直接用于分析或制图,简直就是灾难。

字段计算器的Python函数就像一把瑞士军刀,能帮我们解决这类"脏数据"问题。相比常规的字段计算器,Python函数最大的优势在于可以编写复杂的逻辑判断和字符串处理。举个例子,当需要把"DN300X200"这样的管径值转换为纯数字时,简单的字符串替换函数就显得力不从心了。

我在实际项目中遇到过更复杂的情况:同一个字段里既有"600+400"这样的拼接值,又有"Y2-500"这样的前缀标记,甚至还有直接写"300"的纯数字。这时候就需要用Python写一个"全能型"处理函数,把这些五花八门的格式统一成标准数值。

2. 搭建Python函数的基本框架

2.1 创建自定义函数

在QGIS中打开字段计算器,点击"函数编辑器"选项卡,然后点"+"号新建Python脚本。这里有个小技巧:我习惯用功能描述来命名函数,比如"pipe_diameter_cleaner",这样后期维护时一目了然。

系统会自动生成模板代码,我们需要重点关注三个部分:

from qgis.core import * from qgis.gui import * @qgsfunction( group='Custom', # 函数分组名 referenced_columns=[] # 引用的字段列表 ) def my_function(value1, feature, parent): # 你的处理逻辑 return result

第一次写的时候我犯了个错误:直接删掉了模板里的装饰器参数。结果函数怎么都调用不了,后来才发现@qgsfunction这个装饰器是QGIS识别函数的关键。referenced_columns参数特别有用,当你需要同时处理多个字段时,在这里声明字段名可以避免很多奇怪的报错。

2.2 处理多种数据格式

针对管径字段的各种情况,我写了这样的处理逻辑:

def clean_diameter(raw_value): # 处理X分隔的情况,如300X200 if "X" in raw_value: parts = raw_value.replace("X", ",").split(",") # 处理+分隔的情况,如600+400 elif "+" in raw_value: parts = raw_value.replace("+", ",").split(",") # 处理带前缀的情况,如Y2-500 elif "Y2" in raw_value: parts = [raw_value.replace("Y2", "")] # 纯数字情况直接返回 else: return int(raw_value) # 转换所有部分为整数并取最大值 return max([int(p) for p in parts if p.isdigit()])

这里有个血泪教训:一定要先做类型检查!有次处理数据时,函数突然报错,排查半天发现是有条记录的管径字段居然是NULL值。后来我加上了防御性编程:

if not raw_value or str(raw_value).strip() == "": return None

3. 调试技巧与常见陷阱

3.1 在VS Code中调试

在QGIS里调试Python函数特别不方便,我的解决方案是先在VS Code里测试通过。创建一个测试脚本:

if __name__ == "__main__": test_cases = [ "300X200", # 应返回300 "150+100", # 应返回150 "Y2-500", # 应返回500 "800", # 应返回800 "" # 应返回None ] for case in test_cases: result = clean_diameter(case) print(f"输入: {case} => 输出: {result}")

这样能快速验证各种边界情况。记得测试时要覆盖所有可能的输入格式,包括空值、非法字符等异常情况。

3.2 QGIS特有的数据类型问题

最让我头疼的是QGIS处理数据类型的方式。有次函数在VS Code测试正常,但在QGIS里总是报类型错误。后来发现QGIS传递给函数的值可能是QVariant类型,需要先转换成Python标准类型:

from qgis.PyQt.QtCore import QVariant def clean_diameter(raw_value): if isinstance(raw_value, QVariant): if raw_value.isNull(): return None raw_value = str(raw_value) # 后续处理逻辑...

另一个常见问题是字段引用。如果在函数中需要访问其他字段,必须在装饰器中声明:

@qgsfunction( group='Custom', referenced_columns=['管径', '材质'] # 这里列出所有用到的字段 )

4. 高级应用:自动化筛选管线

4.1 条件筛选的实现

清洗完数据后,我们通常需要按条件筛选要素。比如找出所有管径大于等于600mm的主干管:

@qgsfunction(group='Custom') def is_main_pipe(diameter, feature, parent): try: return diameter >= 600 except: return False

在QGIS中应用这个函数时,可以直接在"按表达式筛选"中使用:

is_main_pipe("管径")

4.2 性能优化技巧

处理大型管网数据时,我发现字段计算可能会很慢。通过这几个方法可以显著提升速度:

  1. 减少类型转换:尽量保持数据在整数类型操作,避免反复转换
  2. 使用生成器表达式:代替列表推导式减少内存占用
  3. 批量处理:对于shapefile,先用Python脚本预处理比在QGIS中逐条计算更快
def batch_clean(input_shp): layer = QgsVectorLayer(input_shp) with edit(layer): for feature in layer.getFeatures(): cleaned = clean_diameter(feature['管径']) feature['管径'] = cleaned layer.updateFeature(feature)

5. 中文编码问题的解决方案

用GDAL/OGR处理中文数据时,经常会遇到乱码问题。这是我总结的解决方案:

# 在脚本开头设置全局编码 gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES") gdal.SetConfigOption("SHAPE_ENCODING", "GBK") # 创建字段时指定编码 field_defn = ogr.FieldDefn('管径', ogr.OFTString) field_defn.SetWidth(50) new_layer.CreateField(field_defn)

如果数据已经在QGIS中显示乱码,可以尝试修改图层属性中的"数据源编码"选项,通常GBK或UTF-8能解决大部分中文问题。

6. 实战案例:完整数据处理流程

假设我们有一个雨水管网数据,需要完成以下操作:

  1. 清洗管径字段
  2. 筛选主干管道
  3. 导出新的shapefile

完整代码如下:

from qgis.core import * from osgeo import ogr, gdal def process_pipeline(input_path, output_path): # 1. 设置中文编码 gdal.SetConfigOption("SHAPE_ENCODING", "GBK") # 2. 打开数据源 datasource = ogr.Open(input_path) layer = datasource.GetLayer(0) # 3. 创建输出文件 driver = ogr.GetDriverByName("ESRI Shapefile") output_ds = driver.CreateDataSource(output_path) output_layer = output_ds.CreateLayer( "main_pipes", layer.GetSpatialRef(), geom_type=ogr.wkbLineString ) # 4. 复制字段定义 for i in range(layer.GetLayerDefn().GetFieldCount()): output_layer.CreateField(layer.GetLayerDefn().GetFieldDefn(i)) # 5. 处理并筛选要素 for feature in layer: diameter = clean_diameter(feature.GetField("管径")) if diameter and diameter >= 600: new_feature = ogr.Feature(output_layer.GetLayerDefn()) new_feature.SetGeometry(feature.GetGeometryRef()) for i in range(feature.GetFieldCount()): if feature.GetFieldDefnRef(i).GetName() == "管径": new_feature.SetField(i, diameter) else: new_feature.SetField(i, feature.GetField(i)) output_layer.CreateFeature(new_feature) output_ds = None # 关闭数据源

这个流程把之前讲的所有知识点都串联起来了。在实际项目中,我还会添加进度打印和异常处理,让脚本更加健壮。

http://www.jsqmd.com/news/646451/

相关文章:

  • 墨水屏项目省电秘籍:用ESP8266深度睡眠+定时刷新(实测功耗对比)
  • Windows/Mac/Linux全平台保姆级教程:从零配置OpenCode到成功调用Gemini-3
  • 从硬件工程师的视角看I2C:为什么开漏+上拉是总线设计的‘最优解’?聊聊功耗、速率与可靠性
  • 如何让点击目标元素时随机移动到页面任意位置
  • 如何为Windows和Linux系统免费获取macOS风格的鼠标指针主题?
  • 大模型时代的技术演进:从Transformer到多模态融合
  • 红帆iOffice.net udfGetDocStep.asmx接口SQL注入漏洞深度解析与防御实践
  • Teamcenter Active Workspace云许可与本地网络许可的混合应用模式
  • 07_NVIDIA Triton Java API:企业级高性能推理服务
  • Origin软件弹窗提示盗版?一个1KB的批处理文件帮你一键搞定(附Hosts修改教程)
  • 2026奇点大会未公开议程泄露:Meta/Adobe/华为联合演示的跨模态图像生成协议,即将改变行业交付标准
  • 开发者副业:从开源贡献到被动收入——软件测试从业者的专业变现指南
  • 如何用Vulkan显存测试工具:3步快速诊断GPU硬件稳定性问题
  • 3分钟掌握微信聊天记录导出:WeChatMsg完全指南
  • 别光抄代码!通过C语言飞机大战项目,真正搞懂数组和全局变量的实战用法
  • 深入解析OpenvSwitch中基于Linux-HTB的QoS多队列限速实践
  • 终极指南:如何用memtest_vulkan快速检测GPU显存稳定性问题
  • apiserver中api的层级与完整构成
  • 图解UEFI启动时,PCIe的‘根’与‘桥’是如何长出来的(以EDK2代码为例)
  • B站视频下载神器:3分钟免费获取B站视频的终极方案
  • Bosch SMI810 IMU传感器驱动开发实战:从SPI通信到数据处理全流程解析
  • Ubuntu22.04装搜狗输入法踩坑实录:从依赖报错到流畅输入的全过程
  • ESP32+MPU6500 DMP模式解析:如何让SG90舵机云台响应又快又稳?
  • ESP32 BLE开发避坑指南:GAP/GATT回调函数里那些容易踩的‘坑’和实战调试技巧
  • Anlogic TD 5.6.1项目创建避坑指南:如何正确设置引脚约束文件
  • 终极解决方案:三步彻底卸载Microsoft Edge浏览器
  • C#进阶-特性全知识点总结
  • 技术演讲恐惧症?3步成为会议焦点
  • 深入Zynq BootROM:揭秘上电后ARM核执行的“第一行代码”
  • Docker+Redis Cluster集群搭建避坑指南:三主三从配置全流程解析