ArcGIS脚本工具03——TXT批量转矢量

一、功能介绍

TXTToFeature:将带坐标点的TXT文件批量转矢量

TXT格式如下:

[属性描述]
坐标系=2000国家大地坐标系
几度分带=3
投影类型=高斯克吕格
计量单位=米
带号=****
精度=****
转换参数=,,,,,,
[地块坐标]
***,****,****/***/,xxxx,,,,,@
J01,1,2971685.152000,38993024.791000
J02,1,2971696.601000,38993013.240000
J03,1,2971695.439000,38992748.939000
J04,1,2971685.152000,38993024.791000

二、实现代码

# -*- coding: cp936 -*-
import arcpy
import os
import sys
import re
import codecs

class TXTToFeatureConverter:
    """TXT转矢量要素转换器"""
    
    def __init__(self):
        """初始化"""
        # 设置环境
        arcpy.env.overwriteOutput = True
    
    def read_txt_file(self, filepath):
        """读取TXT文件,返回文件内容"""
        try:
            # 尝试不同的编码读取
            encodings = ['gbk', 'gb2312', 'utf-8', 'utf-8-sig', 'cp936']
            
            for encoding in encodings:
                try:
                    with codecs.open(filepath, 'r', encoding=encoding) as f:
                        content = f.read()
                    return content
                except UnicodeDecodeError:
                    continue
                except Exception as e:
                    arcpy.AddWarning(u"读取文件失败 {}: {}".format(os.path.basename(filepath), str(e)))
                    continue
            
            # 如果所有编码都失败,尝试二进制读取
            with open(filepath, 'rb') as f:
                raw_data = f.read()
                try:
                    return raw_data.decode('gbk', errors='ignore')
                except:
                    return raw_data.decode('utf-8', errors='ignore')
                    
        except Exception as e:
            arcpy.AddWarning(u"无法读取文件 {}: {}".format(os.path.basename(filepath), str(e)))
            return None
    
    def parse_txt_content(self, content, filename):
        """解析TXT内容,提取地块数据"""
        plots = []  # 存储所有地块数据
        
        if not content:
            return plots
        
        lines = content.splitlines()
        
        in_coord_section = False
        current_attrs = {}
        current_points = []
        current_plot_started = False
        
        for line in lines:
            line = line.strip()
            
            # 检测地块坐标部分开始
            if line == "[地块坐标]":
                in_coord_section = True
                arcpy.AddMessage(u"  找到'地块坐标'标记")
                continue
            
            if not in_coord_section:
                continue
            
            # 处理属性行(地块起始行) - 示例: 135,5.5985,DDE2023020,燕子窝货运码头地块,,G50G024016,工业用地,,@
            if re.match(r'^\d+', line) and ',' in line:
                # 如果已经有一个地块在处理中,先保存它
                if current_plot_started and current_points:
                    # 闭合多边形(首尾点相同)
                    if len(current_points) >= 3 and current_points[0] != current_points[-1]:
                        current_points.append(current_points[0])
                    
                    if len(current_points) >= 4:
                        plots.append((current_attrs.copy(), current_points))
                        arcpy.AddMessage(u"    提取到地块: {} ({}个点)".format(
                            current_attrs.get("受让人", u"未知"), len(current_points)))
                    else:
                        arcpy.AddWarning(u"    地块点数不足 ({}个点),已跳过".format(len(current_points)))
                    
                    # 重置当前地块
                    current_attrs = {}
                    current_points = []
                
                # 解析新地块的属性
                parts = line.split(',')
                try:
                    if len(parts) >= 4:
                        current_attrs["受让人"] = parts[3].strip()
                    if len(parts) >= 7:
                        current_attrs["用途"] = parts[6].strip()
                    current_attrs["源文件"] = filename
                    current_plot_started = True
                    arcpy.AddMessage(u"  发现新地块: {}".format(current_attrs.get("受让人", u"未知")))
                except Exception as e:
                    arcpy.AddWarning(u"    解析属性行失败: {} - 错误: {}".format(line, str(e)))
                    current_plot_started = False
                continue
            
            # 处理坐标行 - 格式: J1,1,2992679.6937,38595657.7082
            if current_plot_started and (re.match(r'^[a-zA-Z]?\d+', line) or re.match(r'^\d+', line)):
                parts = line.replace(',', ',').split(',')
                try:
                    # 格式: J1,1,2992679.6937,38595657.7082
                    # 第三列是北坐标(y),第四列是东坐标(x)
                    if len(parts) >= 4 and parts[0].startswith('J'):
                        y = float(parts[2])  # 北坐标
                        x = float(parts[3])  # 东坐标
                    elif len(parts) >= 4:
                        # 尝试解析最后两个值为坐标
                        y = float(parts[-2])
                        x = float(parts[-1])
                    elif len(parts) >= 2:
                        # 只有坐标值的情况
                        y = float(parts[0])
                        x = float(parts[1])
                    else:
                        continue
                    
                    current_points.append(arcpy.Point(x, y))
                    
                except (ValueError, IndexError) as e:
                    arcpy.AddWarning(u"    解析坐标行失败: {} - 错误: {}".format(line, str(e)))
        
        # 处理文件末尾的地块
        if current_plot_started and current_points:
            # 闭合多边形(首尾点相同)
            if len(current_points) >= 3 and current_points[0] != current_points[-1]:
                current_points.append(current_points[0])
            
            if len(current_points) >= 4:
                plots.append((current_attrs.copy(), current_points))
                arcpy.AddMessage(u"    提取到地块: {} ({}个点)".format(
                    current_attrs.get("受让人", u"未知"), len(current_points)))
            else:
                arcpy.AddWarning(u"    地块点数不足 ({}个点),已跳过".format(len(current_points)))
        
        return plots
    
    def create_output_feature_class(self, output_path, spatial_ref):
        """创建输出要素类"""
        
        # 判断输出类型(shp或gdb)
        if output_path.lower().endswith('.shp'):
            workspace = os.path.dirname(output_path)
            fc_name = os.path.basename(output_path)[:-4]  # 去掉.shp扩展名
            
            # 创建shapefile
            arcpy.CreateFeatureclass_management(
                out_path=workspace,
                out_name=fc_name,
                geometry_type="POLYGON",
                spatial_reference=spatial_ref
            )
            
            output_fc = os.path.join(workspace, fc_name + ".shp")
        else:
            # 创建地理数据库要素类
            arcpy.CreateFeatureclass_management(
                out_path=os.path.dirname(output_path),
                out_name=os.path.basename(output_path),
                geometry_type="POLYGON",
                spatial_reference=spatial_ref
            )
            
            output_fc = output_path
        
        # 添加字段
        arcpy.AddField_management(output_fc, "受让人", "TEXT", field_length=200)
        arcpy.AddField_management(output_fc, "用途", "TEXT", field_length=100)
        arcpy.AddField_management(output_fc, "源文件", "TEXT", field_length=255)
        arcpy.AddField_management(output_fc, "地块序号", "LONG")
        
        return output_fc
    
    def process_folder(self, input_folder, spatial_ref, output_path):
        """处理文件夹中的所有TXT文件"""
        
        # 检查输入文件夹
        if not os.path.exists(input_folder):
            arcpy.AddError(u"输入文件夹不存在: {}".format(input_folder))
            return False
        
        # 获取所有TXT文件
        txt_files = []
        for root, dirs, files in os.walk(input_folder):
            for file in files:
                if file.lower().endswith('.txt'):
                    txt_files.append(os.path.join(root, file))
        
        if not txt_files:
            arcpy.AddError(u"在文件夹中没有找到TXT文件")
            return False
        
        arcpy.AddMessage(u"找到 {} 个TXT文件".format(len(txt_files)))
        
        # 创建输出要素类
        arcpy.AddMessage(u"正在创建输出要素类...")
        output_fc = self.create_output_feature_class(output_path, spatial_ref)
        arcpy.AddMessage(u"输出要素类: {}".format(output_fc))
        
        # 准备插入游标
        fields = ["SHAPE@", "受让人", "用途", "源文件", "地块序号"]
        
        # 统计信息
        total_plots = 0
        processed_files = 0
        failed_files = 0
        plot_counter = 0
        
        # 开始处理文件
        with arcpy.da.InsertCursor(output_fc, fields) as cursor:
            for txt_file in txt_files:
                filename = os.path.basename(txt_file)
                arcpy.AddMessage(u"\n处理文件: {}".format(filename))
                
                try:
                    # 读取文件内容
                    content = self.read_txt_file(txt_file)
                    if not content:
                        arcpy.AddWarning(u"  文件内容为空或读取失败")
                        failed_files += 1
                        continue
                    
                    # 解析地块数据
                    plots = self.parse_txt_content(content, filename)
                    
                    if plots:
                        # 插入每个地块
                        for attrs, points in plots:
                            try:
                                # 创建多边形
                                if len(points) >= 4:
                                    array = arcpy.Array(points)
                                    polygon = arcpy.Polygon(array)
                                    
                                    # 准备属性值
                                    assignee = attrs.get("受让人", u"未知")
                                    purpose = attrs.get("用途", u"")
                                    source_file = attrs.get("源文件", filename)
                                    
                                    # 插入记录
                                    cursor.insertRow([polygon, assignee, purpose, source_file, plot_counter + 1])
                                    
                                    plot_counter += 1
                                    total_plots += 1
                                    
                            except Exception as e:
                                arcpy.AddWarning(u"    创建多边形失败: {} - 错误: {}".format(
                                    attrs.get("受让人", u"未知"), str(e)))
                        
                        arcpy.AddMessage(u"  成功提取 {} 个地块".format(len(plots)))
                        processed_files += 1
                    else:
                        arcpy.AddWarning(u"  未提取到有效地块数据")
                        failed_files += 1
                        
                except Exception as e:
                    arcpy.AddWarning(u"  处理文件失败: {}".format(str(e)))
                    failed_files += 1
        
        # 输出统计信息
        arcpy.AddMessage(u"\n" + "=" * 60)
        arcpy.AddMessage(u"处理完成!")
        arcpy.AddMessage(u"成功处理文件数: {}".format(processed_files))
        arcpy.AddMessage(u"失败文件数: {}".format(failed_files))
        arcpy.AddMessage(u"总地块数: {}".format(total_plots))
        arcpy.AddMessage(u"输出要素类: {}".format(output_fc))
        arcpy.AddMessage(u"=" * 60)
        
        return True


def main():
    """主函数 - ArcGIS脚本工具入口"""
    
    try:
        # 获取参数
        input_folder = arcpy.GetParameterAsText(0)
        spatial_ref = arcpy.GetParameter(1)  # 空间参考对象
        output_path = arcpy.GetParameterAsText(2)
        
        # 验证参数
        if not input_folder:
            arcpy.AddError(u"请输入TXT文件夹路径")
            return
        
        if not output_path:
            arcpy.AddError(u"请输入输出路径")
            return
        
        # 创建转换器对象
        converter = TXTToFeatureConverter()
        
        # 处理文件夹
        success = converter.process_folder(input_folder, spatial_ref, output_path)
        
        if success:
            arcpy.AddMessage(u"\n转换成功完成!")
            
            # 尝试刷新目录
            try:
                arcpy.RefreshCatalog(os.path.dirname(output_path))
                arcpy.AddMessage(u"目录已刷新")
            except:
                pass
        else:
            arcpy.AddError(u"转换失败!")
            
    except Exception as e:
        arcpy.AddError(u"执行错误: {}".format(str(e)))
        import traceback
        arcpy.AddError(traceback.format_exc())


if __name__ == "__main__":
    main()
    

三、参数设置

 # 获取参数
        input_folder = arcpy.GetParameterAsText(0)
        spatial_ref = arcpy.GetParameter(1)  # 空间参考对象
        output_path = arcpy.GetParameterAsText(2)

根据功能代码中输入参数的个数和顺序设置交互页面参数。

如上代码所示,BatchDeleteTables有两个输入参数:参数(0),参数(1),参数(2)。

交互页面设置对应的三个参数:TXT文件所在文件夹路径;投影坐标系;矢量输出路径。

四、功能测试

浏览TXT文件夹所在路径;选择对应的空间参考;设置矢量文件输出路径。

这里测试只放了一个txt文件,多个txt也是正常转的。矢量文件中的字段是根据之前的需求随便写的,可以自行修改。【源文件】记录的是生成当前要素的txt文件名,便于溯源。

这段代码有的时候会显示提取失败,目前还不能确定什么原因导致的。测试过几次,可能是文件被莫名其妙地锁住了,把待转换的txt文件换在别的路径下,又能正常执行了。反正如果有问题就多试几下,换路径或关闭软件重新打开,或者清理一下temp文件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值