ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

ArcGIS属性字段批量更新实战:字段计算器、Join与ArcPy脚本

ArcGIS属性字段批量更新实战:字段计算器、Join与ArcPy脚本 你多半也遇到过这种场景几千个图斑要重新编号地类编码按新规范整体换一遍或者甲方发来一张Excel表让你把里面的补偿金额按“地块编号”关联到图斑属性里去。如果还想着一块一块点开属性表去改我劝你趁早放弃——所谓“Arcgis字段批量更新”说白了就是把这类重复劳动交给字段计算器、关联表、模型构建器和ArcPy脚本去兜底。这篇文章把我这些年整理属性数据用到的方法一次性梳理出来每种方法解决什么问题、怎么写、有哪些坑都会讲清楚。适合正在被属性表折磨的GIS数据处理员、测绘和规划口的同行也适合刚学会打开ArcMap、准备认真做数据的学生。1. 先给批量更新分个类不同需求对应的处理思路完全不同很多人一上来就打开字段计算器结果发现复杂需求根本写不出来。我现在的习惯是先停下问自己一句“这个批量更新到底属于哪一类”因为分类定了工具就定了表达式的写法也定了。1.1 我遇到的五类典型批量更新需求先说结论。日常工作中用到字段批量更新的需求我基本归为五类。编号类图斑流水号、界址点号、调查单元编号。比如地籍调查里要给一批界址点按顺序编号或者给选中的几十个图斑从1开始编号。这类需求特点是“每行一个数且前后有序”。几何计算类重新计算面积、周长、长度、质心坐标。典型场景是投影坐标系变了后发现面积全不对或者甲方要求面积单位从平方米改成亩。标准替换类把旧值域替换成新值域清洗文本空格拼接字段截取编号后三位。比如旧的地类编码“0101”要改成新国标“010101”或者从一串混合文本里抽出后三位作为新编号。关联赋值类从另一张表里把属性带过来。最常见的就是拿Excel表和图斑属性按“地块编号”匹配把补偿金额、权属单位、调查结论等字段灌进图斑。条件派生类根据一个或多个字段的值组合判断生成新字段。比如“面积大于100且地类为耕地的标记为A类其余标记为B类”。针对这五类需求我用一张表总结对应路线后面展开细讲需求类型首选方法备选/进阶方法难度编号类ArcPy游标脚本按字段排序后遍历编号OBJECTID偏移量顺序无要求时中几何计算类字段右键“计算几何”字段计算器写!Shape_Area! * 0.0015低标准替换类字段计算器Python表达式代码块写复杂逻辑低-中关联赋值类Join加字段计算器空间连接Spatial Join中条件派生类字段计算器代码块ArcPy脚本批量处理多个图层中1.2 动手前先检查这四件事能省掉后面一半的返工我见过太多人表达式写到一半才发现字段类型不对、空值一堆或者坐标系数值差异离谱。建议在动手前花五分钟过一遍下面四件事。第一字段类型。文本型字段和数值型字段在字段计算器里的写法完全不一样。数值字段不需要引号文本字段的表达式里必须给字符串值加引号。我常常遇到有人把耕地写成耕地结果字段计算器直接报语法错误。另外文本字段长度也会卡人后面第5章细说。第二空值和重复值。文本拼接时只要有一个字段为空结果往往就变成空值Join关联时如果关联键有重复结果可能错得毫无察觉。所以批量更新前先排序看一遍或者用“按属性选择”里IS NULL的语句查一次空值。第三坐标系和投影。如果你要批量计算面积、长度一定要确定图层用的是投影坐标系。用经纬度的地理坐标系直接算面积单位是平方度数值完全没有意义。更隐蔽的是有人把图层“定义”成错误的坐标系这时候用字段计算器算出来的几何值就是基于那个错误坐标系的改字段怎么都救不回来得先处理坐标系问题。第四备份。我说句实在话字段计算器本身不是“后悔药”。尤其是在编辑会话外直接算一旦算错想回去只能靠备份。我处理重要数据前一定会复制一份到临时地理数据库或者直接在目录树里CtrlC、CtrlV出一个副本。这一步不花时间但能救命。2. 字段计算器入门的核心工具但表达式写对了才算会字段计算器是批量更新最基础的手段学会它就能解决掉一半以上的日常需求。但我也发现很多人的使用方式停留在“点几个按钮”的层次一遇到稍微复杂的条件就束手无策。所以这一章把字段计算器讲透。2.1 打开方式和解析程序的选择为什么建议Python而不是VB Script打开方式很简单。ArcMap里右键属性表里的字段名菜单里选“字段计算器”ArcGIS Pro里同样是右键字段名选“计算字段”。如果图层当前有选中记录字段计算器只会计算选中的记录对话框底部会显示“已选择记录数”这是“给不连续图斑批量赋值”最方便的入口。真正需要留意的是对话框里的“解析程序”下拉框。ArcMap中默认是VB Script旁边才是Python。我强烈建议你手动切到Python理由有三条ArcGIS Pro已经完全没有VB Script了只有Python你迟早要迁过去Python表达式的可读性和可复用性都明显更好网上能搜到的字段计算器示例几乎全是Python语法。特别注意字段计算器的Python语法跟在Python窗口里写脚本不太一样字段名不是变量名而是用英文感叹号包裹。比如引用“地类编码”字段要写成!地类编码!而不是直接写字段名。如果字段名带空格或特殊字符也一样用感叹号包起来。2.2 基础表达式条件替换、字符串拼接、取后三位、去空格下面几个表达式是我在字段计算器里最常用的直接抄作业即可。条件替换把旧地类编码“0101”换成“010101”其他编码保持不变010101 if !旧编码! 0101 else !旧编码!这里就是Python的三元表达式逻辑很直观。注意文本值要加引号。字符串拼接比如把乡镇名和村名拼成“镇-村”格式!乡镇名! - !村名!但这样写有个隐患只要“乡镇名”或“村名”其中一个是空值拼接结果会变成空值。稳妥的做法是给空值兜个底(!乡镇名! or ) - (!村名! or )在Python里空字符串会被当成False所以or 能保证空值字段也返回一个空字符串而不是None。取编号后三位这是最近被问得很多的需求比如有一串长编号只想保留后三位作为新字段str(!编号!)[-3:]Python的字符串切片[-3:]是从倒数第3位取到末尾。注意如果编号本身是数值型字段必须先str()转成字符串才能切片如果编号不足三位比如是“12”这个表达式会返回“12”而不是空或补零。去除首尾空格地类名称里经常混入空格!名称!.strip()如果想连中间的空格一起去掉就用replace( , )。保留两位小数比如对计算出来的面积保留两位round(!面积!, 2)不过这里提醒一句round()在浮点运算下偶尔会出现123.459999这种显示问题别全怪代码这是二进制浮点的老毛病。要彻底展示好看还得在图层属性里设置字段的小数位数。2.3 代码块一个代码块能顶几十个if如果表达式写一长串三元判断读起来和改起来都痛苦。字段计算器对话框下方有一块“Pre-Logic Script Code”翻译过来就是“预逻辑脚本代码”。你可以把自定义函数写在这里表达式那一栏只需调用函数名。举个例子要根据“地类编码”和“面积”组合判断地块类别def get_class(dl, area): if dl 0101 and area 100: return A类 elif dl 0101: return B类 else: return C类表达式栏写get_class(!地类编码!, !面积!)这样逻辑一清二楚后面要改判断规则只动代码块里的函数就行不用去改那串又长又绕的表达式。代码块里也可以import标准库比如处理日期、做正则匹配。但我的经验是不要在里面做太复杂的事尤其是别尝试arcpy去查询别的图层或开编辑会话字段计算器是逐行调用的稍不留神就会卡死整个程序。复杂批处理的事交给第4章的ArcPy脚本。2.4 关于编号从1递增字段计算器的短板和绕开方案网上很多教程会告诉你用代码块里的global变量做自增编号rec 0 def autoIncrement(): global rec rec 1 return rec我劝你不要迷信这个写法。字段计算器对每个要素是独立调用函数的全局变量在原理上能不能保持状态取决于版本和运行方式我在ArcMap里实测过数据量一大或者开了后台处理经常出现编号重复、乱序甚至直接报错。编号这种事老老实实换工具。如果你只是要一个“按现在表里的顺序”来的连续编号且不追求空间顺序直接用OBJECTID最省事。ArcMap的FID和ArcGIS Pro的OBJECTID都是从0或1开始的系统字段写!OBJECTID! 1就能得到从1开始的顺序号。缺点是删除过要素后会有断号如果甲方不要求连续是完全够用的。如果你要“选中的图斑从1开始编号”或者要按面积从大到小编号字段计算器就彻底不合适了得用ArcPy游标这部分我放在第4.3节讲那里有可以直接跑的脚本。3. 跨表批量更新Join是临时视图计算字段才能落库字段计算器再强也只在图层自己的字段里打转。但实际工作中属性数据的来源往往是另一张表——Excel调查表、其他图层的属性、数据库里的代码表。把这张表的数据批量灌进目标图层是字段批量更新里最容易踩坑的一类。3.1 什么时候需要跨表更新最典型的场景就是外业调查回来后把PDA或Excel里的调查结果按“地块编号”匹配到图斑上。还有一种是不同业务科室维护不同图层比如一份是基础地形一份是权属表现在要把权属信息带进地形图斑。有人直接打开Excel看着每一条记录往图斑属性表里复制粘贴。小数据量可以几百上千条以后基本就是在挑战耐心和视力。跨表更新的正规思路是两步先用“连接Join”把两张表临时关联起来再用字段计算器把关联表里的值算进目标字段。记住一个关键认知Join只是“临时视图”它不会真正修改图层里的属性。你只有通过字段计算器把关联字段的值写回目标字段数据才算真正落库。3.2 完整操作连接、计算、移除连接、导出我按实际操作的顺序拆解一遍。第一步准备关联表。如果来源是Excel强烈建议先转成地理数据库表或CSV再连接后面会解释原因。打开ArcMap或Pro的目录树右键目标地理数据库或文件夹选择“导入”把Excel转成表。同时检查关联键两边是文本型就一定都要是文本型数值型就都要是数值型别一边文本一边数值。第二步在目标图层上建立连接。右键图层选“连接和关联”里的“连接”。连接类型选“基于某一字段的链接”目标图层的字段选“地块编号”连接表的字段也选“地块编号”。确定后打开属性表你会看到右侧多出来一堆带后缀的字段比如“Excel表.权属单位”。第三步用字段计算器写回目标字段。如果你要在目标图层的“权属单位”字段里写入关联表的“权属单位”值打开字段计算器在字段列表里找到带表名前缀的字段双击把它插入到表达式里形式像这样!Excel表.权属单位!这里我特别强调不要手敲带表名的字段引用一定从对话框的字段列表里双击插入。因为表名里很可能带点号、美元符号、空格等特殊字符手敲极易出错。第四步移除连接。右键图层选“连接和关联”里的“移除连接”。移除后属性表里那些带前缀的字段会消失但目标字段已经被写入了。第五步导出新数据。移除连接只是把临时视图去掉为了确保结果稳定我通常右键图层选“数据”里的“导出数据”生成一份新要素类。这样就算原始连接表路径变更也不会影响成果数据。3.3 连接后最容易翻车的几个细节先说一对多问题。如果关联表里“地块编号”出现重复Join后每个图斑可能匹配到多条记录取哪条全靠运气结果可能错得悄无声息。所以关联前先在Excel里对关联键做去重或者用“汇总统计”工具先看看重复情况。再说字段类型不一致。一方是文本“001”另一方是数值1Join会匹配失败看起来连上了实则全是空值。检查办法是Join后随便选几个图斑看关联字段有没有值。接着是Excel表名问题。工作表转进ArcGIS后默认表名是“Sheet1$”字段计算器里引用!Sheet1$.权属单位!经常因为美元符号引发语法错误。所以我才说先转成地理数据库表或CSV一劳永逸。最后是字段重名。两个表都有“名称”字段时Join后目标图层的字段会变成“图层名.名称”和“关联表.名称”计算时别选错。这个看字段列表就能发现。3.4 按空间位置带属性空间连接的思路如果需要按空间位置批量更新字段而不是按属性键关联SearchJoin的思路更合适。比如给每个点赋予它所在面片的属性或者按最近距离匹配道路等级。操作上用“空间连接”工具输入目标要素和带属性的源要素匹配选项选“相交”“包含”或“最近”输出会生成一个新的要素类里面带着源要素的属性。空间连接的好处是不用管关联键坏处是它会生成新数据不是原地更新。如果你必须原地更新可以再把空间连接结果按OBJECTID连接回原图层用第3.2节的方法算回字段。4. 多文件批量更新模型构建器先兜底ArcPy脚本才是最终答案单个图层玩明白以后真正的痛点是“一个文件夹里几十个要素类每个都要做同样的字段更新”。这时候再手工去点字段计算器哪怕每层只要一分钟总量也够你喝一壶。批量处理有两条路模型构建器和ArcPy脚本。4.1 模型构建器迭代要素类加计算字段可视化完成批处理模型构建器的好处是站在ArcGIS图形化肩膀上拖拖拽拽就能做循环适合不会写代码但想省时间的用户。核心思路是这样模型里放一个迭代器让它循环扫描工作空间里的所有要素类每循环一次就把当前这个要素类喂给“计算字段”工具执行同样的字段更新表达式。具体操作上新建一个模型右键空白处选“迭代器”里的“迭代要素类”设置工作空间文件夹再把“数据管理工具”里的“计算字段”拖进模型连接迭代器输出的“要素类”变量到计算字段的“输入表”参数。计算字段工具里的“字段名”和“表达式”如果你想做成通用模板可以右键设为“模型参数”这样每次运行都可以手动指定。热搜里很多人问“模型构建器里创建变量、设置环境工作空间在哪里找出来”我在这里直接回答在模型构建器的菜单栏上点“模型”下拉菜单里面有“环境设置”弹出来以后展开“工作空间”在这里设置“当前工作空间”和“暂存工作空间”。迭代器扫描的路径、中间数据的输出位置都受这个环境控制。我见过不少同事模型运行失败都是因为环境工作空间没设好输出位置指向了默认临时目录最后连结果都找不到。不过模型构建器一旦遇到条件逻辑就笨拙起来。比如“如果字段不存在就先添加字段再计算”或者“如果要素类数量过百就跳过去”这种分支在模型里要做一堆“解析路径”“仅模型工具”的判断又乱又难维护。所以模型适合处理简单的重复性任务复杂一点的我的态度很明确写脚本。4.2 ArcPy脚本批量计算字段基本写法和游标用法ArcPy脚本的第一步是会用“计算字段”工具的函数版。在ArcMap 10.x里写法是import arcpy fc rC:\data\地类图斑.shp arcpy.CalculateField_management(fc, 面积_亩, !Shape_Area! * 0.0015, PYTHON_9.3)在ArcGIS Pro 3.x里写法几乎一样只是解析器参数改成PYTHON3import arcpy fc rC:\data\地类图斑.shp arcpy.management.CalculateField(fc, 面积_亩, !Shape_Area! * 0.0015, PYTHON3)注意一个容易混淆的点表达式里如果写的是文本值必须加引号。比如把“用途”字段批量改成“商业区”arcpy.management.CalculateField(fc, 用途, 商业区, PYTHON3)这里“商业区”在Python里是一个带引号的字符串字面量CalcField会把它解析成文本值。漏掉内层引号就会报错。真正让脚本威力翻倍的是遍历工作空间里的所有要素类import arcpy, os ws rC:\data\成果 arcpy.env.workspace ws expr_map { 新地类: !旧地类! -01, 是否合规: 是 if !面积! 100 else 否 } for fc in arcpy.ListFeatureClasses(): existing [f.name for f in arcpy.ListFields(fc)] for field, expr in expr_map.items(): if field not in existing: arcpy.management.AddField(fc, field, TEXT, field_length50) arcpy.management.CalculateField(fc, field, expr, PYTHON3) print(fc, 处理完成)这个脚本把所有shp文件都找出来判断目标字段是否存在不存在就先添加再按表达式批量计算最后打印出哪些文件处理完了。字段批量更新一旦写成这种脚本几十个要素类也就是几秒钟的事。4.3 连续编号、按顺序编号的脚本实现第2.4节里我留了个话头真正可靠的编号脚本在这里。最简单的连续编号不要求任何顺序import arcpy fc rC:\data\地块.shp i 1 with arcpy.da.UpdateCursor(fc, [编号]) as cursor: for row in cursor: row[0] i cursor.updateRow(row) i 1这段脚本用的是数据访问模块的UpdateCursor它会一行一行遍历属性表读取“编号”字段的值把当前计数i写回去然后i加1。因为游标遍历的是表里的存储顺序结果就是从1开始的连续编号。如果既要有编号顺序又要按面积从大到小就加上SQL排序子句import arcpy fc rC:\data\地块.shp i 1 with arcpy.da.UpdateCursor(fc, [编号], sql_clause(None, ORDER BY 面积 DESC)) as cursor: for row in cursor: row[0] i cursor.updateRow(row) i 1这里sql_clause是一个元组第一个元素是where子句第二个元素是order by子句。让SQL按面积排好序再游标遍历编号顺序就和面积顺序一致了。不过我要提醒一句sql_clause不是所有数据源都支持shp和地理数据库要素类一般没问题某些企业级数据库可能要换个思路。至于“给选中的图斑从1编号”在ArcMap的Python窗口里运行这段脚本输入的是图层名而不是要素类路径并且确保图层上有选择集import arcpy lyr 当前图层 # 内容列表里的图层名不是shp路径 i 1 with arcpy.da.UpdateCursor(lyr, [编号]) as cursor: for row in cursor: row[0] i cursor.updateRow(row) i 1在这个写法的设定下游标会尊重图层的选择集只遍历选中的要素。如果发现它把全表都改了十有八九是把“图层名”传成了“要素类路径”。保险起见也可以在脚本开头用arcpy.SelectLayerByAttribute_management手动指定选择集再放心跑。我建议第一次用这种脚本前先在属性表里选中一小批记录测试确认改的只是选中的部分。4.4 选型建议什么情况用模型什么情况写脚本用模型还是脚本我按自己的习惯给了个表考虑维度模型构建器ArcPy脚本学习成本低拖拽为主中高要懂Python循环批处理适合迭代器直观适合代码更简洁条件分支繁琐模型图会乱容易if语句即可字段存在性判断要建“解析路径”再判断麻烦一行ListFields搞定复用与分享可存成工具适合给同事用可做脚本工具但需维护环境排错日志不直观常看不到哪一步失败print打印每步结果问题定位快我的经验大致是纯重复性任务一两个表达式就能解决的模型构建器确实够用还能变成“绿色小工具”给不懂代码的同事用。但只要你发现自己开始为“该不该跳过这个字段”“这个文件为什么没生成”这种问题折腾模型就可以果断转脚本了。脚本的可控性和排错效率是模型比不了的。5. 字段批量更新的隐藏坑类型、长度、许可和“后悔药”很多字段批量更新做完结果不对其实不是方法没用对而是根本没踩中那几个隐藏的坑。我把最折磨人的几个单独拎出来说。5.1 字段类型与长度的隐形限制字段类型不匹配是批量更新失败里最常见的原因。数值字段里写文本、文本字段里写数字字段计算器往往会给出一个含糊的“计算错误”或者干脆不报错但结果全是空值。我遇到过最坑的一次是文本字段“地类编号”只有10个字符长度我用代码块拼接出12位的新编号ArcMap没报错数据却被静默截断了。等到汇总时才发现几百条编号长度不对。解决方法很笨但有效计算前先打开字段属性看一眼类型和长度。如果是拼接类需求预估一下结果长度字段长度不够就先重新添加一个长度充足的字段再把它设为目标字段。关于“超过最大记录长度”这类提示多半也是目标字段长度不够导致的别硬算先扩容字段。另外空值对文本拼接的影响我之前提过这里再强调一次。字段太干净不代表安全越干净越要警惕空值悄悄让结果变成NULL。最好的防御就是在表达式里给每个参与拼接的字段加or 兜底。5.2 编辑会话、选择集与撤销策略字段计算器和编辑会话的关系很多初学者分不清。我直接说结论属性字段的批量更新不一定非要进入编辑状态在属性表里直接右键字段计算是可以执行的。但如果你在编辑会话里执行那么计算完可以按CtrlZ撤销这是ArcMap和Pro相对良心的地方如果在编辑会话外执行就没有后悔药了。大批量更新时我不建议依赖撤销。别问我为什么知道——有一次我在ArcMap里对几万条记录跑字段计算器算完发现表达式漏了个条件直接按CtrlZ结果程序愣是卡了十几分钟最后还是用备份恢复的数据。所以宁可算之前复制一份副本也不要指望撤销。还有那个隐藏很深的“选择集”逻辑字段计算器默认只计算当前选中的记录。如果你以为自己做了全表批量更新结果只更新了选中的几条那一定是属性表里残留着某次操作的选择记录。反过来想只更新特定区域又忘了选择直接全表覆盖同样欲哭无泪。所以动手前三秒先看一眼属性表底部是否显示“已选取N条”根据实际需要决定要不要清除选择集。5.3 版本和许可差异Desktop、Pro和Server环境下要留意的点ArcMap和ArcGIS Pro的字段计算器Python版本不同语法细节也有一点差异。ArcMap底层是Python 2表达式里的字符串处理、中文编码偶尔会闹别扭Pro底层是Python 3规则更严格旧版表达式里的一些写法会跑不通。我自己碰到过最直观的差异是解析器名称ArcMap的脚本里写PYTHON_9.3到Pro里就得改成PYTHON3不然工具直接报“未知解析器”。许可方面也要有心理准备。如果你面对的是企业级地理数据库SDE里的数据批量更新会受到版本化编辑权限限制没有足够的权限时工具可能直接报“无法编辑”或者静默失败。另外一些功能模块对许可版本有要求比如某些空间处理工具在基础许可下不可用我写过脚本用arcpy.CheckExtension事先判断扩展许可是否存在避免跑到一半才爆许可错误。如果你是在ArcGIS Server或自动化环境中跑批处理还要注意运行账户对数据库、工作空间的访问权限这个和桌面端交互操作完全是两码事。5.4 我常用的回退方案先做辅助字段再做正式字段最后贡献一个我自己的固定操作流程。凡是重要字段的批量更新我不直接改原始字段而是分成三步走第一步添加一个临时辅助字段比如叫“新编号_临时”先把计算结果写进辅助字段。第二步检查辅助字段的结果——排序、统计、抽几条对比源数据确认无误。第三步把辅助字段的值赋给正式字段确认没问题后再把辅助字段删掉。这个习惯看起来多了一步实际上帮我避掉了无数返工。因为字段计算器不像Excel有清晰的历史记录一旦直接覆盖正式字段比较结果就只能靠记忆。有了辅助字段你可以随时打开属性表左右两列摆在一起对比错了也只是删掉辅助字段重来原始数据毫发无损。等这套流程跑顺了你会发现它比“复制备份”更方便也更符合数据处理的习惯。最后再分享一个我的小习惯把常用的字段计算表达式和脚本片段单独存一个txt文件按“面积亩换算”“编号取后三位”“连续编号”“Join后写回字段”分类整理每次遇到新数据直接复制改字段名。别小看这个动作字段批量更新这类活真正耗时间的从来不是计算本身而是你一次次去翻文档、想表达式、排坑。把这些模板攒下来下次半小时的活能压到五分钟。如果你也有什么好用的公式或者踩过的坑欢迎在评论区聊聊说不定能拼出一个更全的模板库。
返回列表