简介:这款ARCGIS编号小工具专为地理信息系统(GIS)用户打造,解决mdb、gdb和shp三类常见数据格式的要素顺序编号问题,适用于批量管理地理要素、快速生成标识码等场景。资源包共74个文件,包含32个dll、32个xml、4个config、4个exe和2个manifest,大小约7.1MB;dll多为ESRI.ArcGIS核心组件,如Geodatabase、Geometry、Carto等,xml为配置说明,exe是可直接运行的主程序。依托ArcGIS API实现地理数据库读写与几何对象处理,用户可自定义编号字段和起始值,对图层进行批量或定向更新,省去手动录入的繁琐环节。已有3991人学习下载,适合从事数据分析、地图制图与空间规划的GIS工作者,能有效提升数据预处理效率,属于即下即用的实用桌面工具。
1. 为什么 ArcGIS 里编个号也要专门做个小工具
做过一次外业图斑的应该都有体会:数据采集完、属性表填完,要打印附图时才发现编号根本没到位。我见过 A同学对着几千行要素手动下拉填充,填到一半复制错行,整张图的编号全乱;也见过某开发者用字段计算器直接FID+1,出来全是负数。编号这事看着简单,实际上它决定了后面制图、出表、入库、接边每道工序能不能对上号。与其每次现想方案,不如把一套「顺序编号→断号检查→重复校验」的流程固定下来。这个 ARCGIS 编号小工具解决的就是这件事:在 ArcGIS 里给矢量数据做可重复、可检查、可交付的顺序编号,覆盖新手能直接跑的字段计算器方案,也覆盖熟手需要的 ArcPy 脚本与断号排查。下面从最常用的字段计算器说起,再到脚本批处理和避坑,全程按我平时落地的顺序来写。
2. 字段计算器快编:十分钟搞定顺序编号,先确认三个前提
2.1 顺序编号的核心思路:seq() 与自增变量
字段计算器是很多人第一个接触的编号工具,但真用起来,十个里有八个翻车。常见做法是在表达式框里写FID + 1,这个思路在简单情况下能用,可一旦你删过数据、做过筛选,FID 中间就有空洞,编号会断,而且 FID 在编辑会话里可能根本不连续。真正稳定的做法是给字段计算器一个"记忆变量",让它自己记住上次编到几。
ArcGIS 字段计算器左侧的"预逻辑脚本代码"框里可以写一小段 Python,右侧表达式框调用它。最常见的写法是:
# 预逻辑脚本代码框里定义 tick = 0 def seq(): global tick tick = tick + 1 return tick# 表达式框里调用 seq()这段逻辑的关键在于:tick是模块级变量,字段计算器对每条记录执行一次表达式时,这个变量不会重置,所以每次调用seq()都会比上次大 1。和FID+1的根本区别是:它不依赖数据本身的行号,而是从 0 开始自己计数,删过行也不会断号。
用的时候有几个参数要确认。第一,字段类型必须是短整型或长整型,如果你拿文本字段跑这个表达式,返回的整数会被自动转成字符串,通常没问题,但如果你对格式有要求,得在脚本里先做格式化。第二,右侧表达式框里只要写seq()这几个字符,不要把def seq():也抄进去。第三,跑之前确认这个图层没有启动编辑器,或者启动了编辑器但还没保存,因为编号写进属性表后如果编辑器里回滚,你可能会误以为脚本写错了。
2.2 前缀、区段和小数位:把编号编成能直接打印的格式
实际制图时,没有人想要光秃秃的 1、2、3。图斑编号通常是BX-2024-001这种组合:前缀区分项目区,年份区分批次,三位数保证排序。字段计算器完全能做这件事,只要把表达式从纯数字改成字符串拼接。
# 预逻辑脚本代码框 tick = 0 def bh(prefix, year): global tick tick = tick + 1 return "{0}-{1}-{2:03d}".format(prefix, year, tick)# 表达式框里调用 bh("BX", 2024){2:03d}是 Python 格式化语法里的位数控制:03d表示至少三位、不足补零,所以第 1 条会编成BX-2024-001,第 12 条是BX-2024-012。这个写法比先编完数字再手动加前缀省很多事,而且打印的时候排序规则很友好,001 一定排在 002 前面。
但这里有一个隐藏很深的坑:如果你用的是"按属性选择"后的选择集,字段计算器默认是对全表执行,不是只算选中的要素。很多人选中 50 个图斑,跑完发现整个图层 1000 个要素全被编了号。解决方法是提前把选中要素导出成一个新图层,在导出后的图层上跑编号,编完再替换回去;或者在 ArcGIS Pro 里把计算器右上角的"应用"范围明确改成"仅所选要素"。另外,如果你的数据源是企业级地理数据库(SDE)里的版本化图层,字段计算器常常报"字段不可编辑"或"对象被锁定",我一般不会在这种图层上直接跑编号,而是先导出到文件地理数据库再处理。
还有一个容易被忽略的前提条件:编号字段不能有重复值约束。如果字段上建了唯一索引,而表里已经有几条脏数据占用了编号,跑一半会直接报错中止,前面的编号还留在表里。所以用字段计算器之前,先确认目标字段是空的、没建唯一索引,或者先做一次重复值清理。三个前提总结下来:非 SDE 版本化数据、目标字段类型正确且为空、计算范围理解清楚。满足这三条,字段计算器方案是最快的一招。
3. ArcPy 重排编号:把一次手工操作变成可重跑的工序
3.1 用 UpdateCursor 做全表重排
字段计算器适合小数据量、一次性编号。但当图层有几千上万个要素,或者你需要在不同批次数据间来回重跑,手工点计算器就不够看了。ArcPy 脚本可以把这个过程变成一个可重复执行的工序:双击运行、输入参数、拿到结果。重排编号最常用的接口是arcpy.UpdateCursor或者arcpy.da.UpdateCursor,后者速度更快,也更接近现代写法。
import arcpy fc = r"C:\project\data.gdb\parcels" # 目标要素类 sort_field = "OBJECTID" # 排序依据字段 number_field = "BH" # 要写入编号的字段 # 关键技术点:先收集排序好的 OBJECTID 列表,再按这个列表逐条更新 order = [row[0] for row in arcpy.da.SearchCursor(fc, ["OBJECTID"], sql_clause=(None, "ORDER BY " + sort_field))] i = 1 with arcpy.da.UpdateCursor(fc, ["OBJECTID", number_field]) as cursor: for oid, _ in cursor: if oid in order: cursor.updateRow([oid, i]) i += 1这里花了我不少时间才想明白一个逻辑:UpdateCursor遍历要素时,返回顺序在多数情况下是按 OBJECTID,但严格说游标的结果顺序不是官方保证的,一旦你有过滤器、连接或视图,顺序就可能是乱的。所以我先用SearchCursor配 SQL 的ORDER BY生成一个排好序的 OBJECTID 列表,然后在更新游标里逐个对比。这种方法牺牲了一点点内存,但顺序绝对可控。
参数上要注意两处。第一,sql_clause的第二个元素必须是字符串元组,格式(None, "ORDER BY 字段名"),前一个元素是 WHERE 子句,不需要就写None。第二,i是 Python 整数,如果你目标字段是文本型,cursor.updateRow传整数进去会自动转字符串,但建议你提前统一类型,避免长整型字段写入了超过字段精度的值。
3.2 加前缀、留位数:把编号标准化成 BX-2024-001
全表重排只解决了"顺序"问题,实际交付时你通常还需要带上项目标识和位数控制。ArcPy 脚本里做这件事比字段计算器更直观,因为所有格式逻辑都写在明面上,下次换需求改一处就行。
import arcpy fc = r"C:\project\data.gdb\parcels" prefix = "BX" # 项目前缀,按需更换 year = 2024 # 批次年份 sort_field = "OBJECTID" number_field = "BH" order = [row[0] for row in arcpy.da.SearchCursor(fc, ["OBJECTID"], sql_clause=(None, "ORDER BY " + sort_field))] i = 1 with arcpy.da.UpdateCursor(fc, ["OBJECTID", number_field]) as cursor: for oid, _ in cursor: if oid in order: bh = "{0}-{1}-{2:03d}".format(prefix, year, i) cursor.updateRow([oid, bh]) i += 1这段代码的逻辑和第 2 章的计算器表达式一致,但好处是它不受"编辑会话""选择集"这些交互状态影响,双击跑就完事。常见做法是把这份脚本存成.py文件,在 ArcGIS 自带的 Python 环境里用命令行执行:
python renumber.py跑之前要检查几个参数:fc的路径如果是要素类,直接写到要素类;如果是文件地理数据库里的数据集,路径写法是gdb路径\数据集名\要素类名。sort_field一般用 OBJECTID 就行,但如果你希望按行政区代码排序后再编号,把ORDER BY后面的字段换成那个字段。有一点要注意:如果多个要素的排序字段值相同,它们的相对顺序是不确定的,所以需要再加一个字段做次级排序,写成ORDER BY 字段1, 字段2。
这套脚本我通常会多设计一个"起始编号"参数,因为有些项目要求从上一批的末尾继续编,而不是每批都从 1 开始。把这个参数暴露成变量,用i = start_number初始化就行。另外,脚本跑完不会像字段计算器那样弹窗告诉你编了多少条,所以末尾最好加一句print("编号完成,共重排 %d 条要素" % (i - start_number)),方便在命令行里确认结果。
4. 断号与重复检查:提交成果前那段必须跑的校验
4.1 断号检测脚本:把要打印的号段拉出来扫一遍
编号编完了,制图也没问题,但最怕的是提交时发现中间断了号。图斑删除、筛选遗漏、字段计算器半途报错,都会造成"看着有序、实际有洞"的情况。断号在属性表里很难一眼看出,尤其是三位数以上的编号。我一般会在提交前跑一段独立的断号检查脚本,它不修改任何数据,只输出缺失的号段。
import arcpy fc = r"C:\project\data.gdb\parcels" fields = ["BH"] existing = set() with arcpy.da.SearchCursor(fc, fields) as cursor: for row in cursor: if row[0] is not None: existing.add(str(row[0])) # 从编号中提取数字部分,BK-2024-001 -> 1 numbers = [] for val in existing: parts = str(val).split("-") try: numbers.append(int(parts[-1])) except ValueError: continue missing = [] for i in range(1, max(numbers) + 1): if i not in numbers: missing.append(i) print("缺号数量:", len(missing)) print("缺号列表:", missing[:100])这段脚本的原理是:先把全部编号收集成一个集合,然后从编号字符串里切出最后一段数字,做一个 1 到最大值的全量对比。集合去重天然解决了重复问题——如果某个编号重复出现,集合里只有一个,但断号校验会因此漏掉重复项,所以重复检查要单独写一段,别混在一起。
参数上要注意的是分隔符。我用的是 BMP 短横线-,如果你的编号用的是下划线或点号,记得改split的参数。另一种常见情况是纯数字编号不带前缀,那parts[-1]取到的还是数字本身,逻辑不受影响,只是int()转换前最好加一层strip()去掉空格。
4.2 重复号与超长号:一起查清楚
断号检查只解决"有没有洞",重复号是另一个问题,而且更隐蔽。很多项目提交时要求编号唯一,但如果表里有两行要素都编成了BX-2024-001,断号检查根本发现不了,因为 1 这个数字在集合里只出现一次,看起来是连续的。所以我对每个编号工具包都会附带一段重复与长度检查脚本,一次性把三类问题列出来。
import arcpy from collections import Counter fc = r"C:\project\data.gdb\parcels" fields = ["BH"] vals = [] with arcpy.da.SearchCursor(fc, fields) as cursor: for row in cursor: if row[0] is not None: vals.append(str(row[0])) counter = Counter(vals) dups = {k: v for k, v in counter.items() if v > 1} print("重复编号:", dups) too_long = [v for v in set(vals) if len(v) > 15] print("超长编号:", too_long)这里的 Counter 是 Python 标准库的计数工具,dups字典的键就是重复的编号,值是出现次数。超长编号的判断标准取决于你的字段长度——如果编号字段是文本型且宽度只有 20,而编号本身有 18 位,看起来能放下,但打印时字体稍大就会溢出图框。我一般把长度阈值设成字段宽度的 80%,留出余量。
这两段脚本可以合并成一个check_number.py文件,跑完会同时输出缺号、重复、超长三类信息。如果三项都为空,打印结果干净到只有一行检查通过,这是我最喜欢的输出。如果发现问题,就回到第 3 章的重排脚本再跑一次,然后重新检查,直到通过。这套流程不复杂,但能挡掉很多交付后才发现的尴尬。
5. 避坑记录:编号工具最常见的五个翻车现场
5.1 科学计数法:长整型编号变成了 1.0000E+10
现象:字段计算器编完序号,打开属性表发现编号显示成1.2345678901E+10,排序全乱。
原因:目标字段是浮点型或双精度,Python 整数写入浮点字段后被自动转成科学计数法显示;或者编号整体超过了短整型的上限 32767,字段自动溢出。
解决:编号字段务必用长整型或文本型,不要用浮点型。如果已经建好字段,先新建一个长整型字段跑编号,跑完再删除旧字段;如果必须保留原字段名,可以用 ArcPy 的AlterField改属性,但更省事的做法是删掉重建。
5.2 选择集没生效:只给选中要素编号,结果全表都被改了
现象:在某图层上按行政区框选了一批图斑,只想给这批编BX-2024-001起的号,字段计算器跑完发现整个图层的 2000 个要素全部被编了号。
原因:字段计算器默认范围是"所有记录",选择集是否符合预期取决于版本和界面选项,很多版本根本不认选择集。
解决:在 ArcGIS Pro 里,打开计算器前先在"应用"下拉框里选择"仅所选要素";或者在 Arcmap 里,先执行arcpy.SelectLayerByAttribute_management把选中要素导出成临时图层再跑。用脚本方案则天然规避这个问题,因为重排脚本只处理传入的fc,不会误伤其他数据。
5.3 编辑器没保存:退出后编号全部回滚
现象:开着编辑会话编完号,看着属性表里一切正常,关闭工程时弹窗提示保存,随手点了"不保存",第二天打开数据编号全没了。
原因:字段计算器在某些版本里如果编辑器处于开启状态,写入属于编辑操作,必须显式保存编辑才能落盘。
解决:跑编号前先看 ArcGIS 界面顶部有没有"保存编辑"按钮,有就先停掉编辑会话;脚本方式不受影响,因为 ArcPy 的UpdateCursor走的是自提交事务,关闭游标即生效。我个人的习惯是跑完脚本后再打开属性表抽查三五个要素,确认值已经写入再关工程,这个习惯救过我很多次。
5.4 删除图斑后不重排
现象:某项目初期编了 300 个号,后来外业复核删掉 20 个无效图斑,剩下的编号变成 1、2、4、5、7,打印附图时缺号明显。
原因:删除要素不会自动触发编号重排,编号只是属性值,不是行号。
解决:交付前强制跑一次断号检查脚本,发现缺号就运行重排脚本把剩余的要素重新从 1 开始编号。这里有个经验:重排前先跟下游确认有没有其他表通过编号关联,如果有,关联表也要同步更新,否则编号一变,外业调查表就对不上了。
5.5 版本化图层直接改编号失败
现象:SDE 里的要素类,跑 ArcPy 脚本编号时报Editing may not be performed in this environment,但同一个脚本在文件地理数据库上跑得好好的。
原因:企业级地理数据库的版本化图层有独立的事务和锁机制,需要先注册版本并启动编辑会话,ArcPy 裸脚本默认进不了版本化编辑环境。
解决:不要在版本化图层上直接跑编号。常见做法是把数据复制或导出到文件地理数据库,编号完成后通过truncate + append或者 ArcGIS 的"数据加载"工具写回去。如果必须在 SDE 里改,就用 ArcGIS Pro 的编辑会话手动跑字段计算器,或者用arcpy.da.Editor包一层编辑会话,但那个复杂度已经不划算了。
6. 把编号工具封装成脚本工具:参数怎么设才能让外业同事也敢点
6.1 三个核心参数与脚本骨架
命令行脚本在你自己机器上好用,但交给不懂代码的同事,双击.py文件往往换来一句"闪了一下啥也没发生"。所以我会把重排脚本封装成 ArcGIS Pro 的脚本工具(Script Tool),把可变的地方全部暴露成工具参数,别人只需要在弹窗里选图层、填前缀、点确定。核心参数就三个,不要贪多:
| 参数名 | 类型 | 是否必填 | 说明 |
|---|---|---|---|
| 目标要素类 | 要素图层 | 是 | 可以是图层名或要素类路径,从下拉框选 |
| 排序字段 | 字段 | 是 | 默认 OBJECTID,也可选行政区代码等 |
| 起始编号 | 长整型 | 否 | 默认 1,续编号时填上一批的末位 +1 |
把脚本参数接进来,需要在脚本里用arcpy.GetParameterAsText接收界面值,再把之前硬编码的路径和变量替换成参数:
import arcpy fc = arcpy.GetParameterAsText(0) sort_field = arcpy.GetParameterAsText(1) start_number = int(arcpy.GetParameterAsText(2) or 1) order = [row[0] for row in arcpy.da.SearchCursor(fc, ["OBJECTID"], sql_clause=(None, "ORDER BY " + sort_field))] i = start_number with arcpy.da.UpdateCursor(fc, ["OBJECTID", "BH"]) as cursor: for oid, _ in cursor: if oid in order: cursor.updateRow([oid, i]) i += 1 arcpy.AddMessage("编号完成,共处理 %d 条要素" % (i - start_number))要点有两个。第一,工具面板里把"目标要素类"的参数类型设为"要素图层",这样用户可以从内容列表直接选择图层,不会填错路径;"排序字段"设成"字段"类型后,ArcGIS 会自动给一个下拉列表,限制用户只能选你指定的字段。第二,脚本里原来写死的number_field = "BH"没有暴露成参数,原因是这个字段在多数项目里是统一约定,暴露出去反而增加选错字段的概率,改字段名时直接改脚本源码就好。
6.2 输出验证与重跑习惯
工具封装完成,不代表可以无脑点。我会在工具执行的最后加一段校验逻辑:跑完编号后立即调一次断号检查函数,如果检查出缺号,直接给红色警告图标,而不是让用户自己去查。这个习惯来自一次真实教训——脚本确实跑完了,但排序字段里有空值,导致部分要素被排到末尾,编号出现空洞,而界面显示"已完成",用户信以为真。从那以后我每次给新图层做编号,都会强制走一遍:先备份要素类→跑重排工具→跑断号检查→抽查三五个要素看格式,确认无误再进入出图流程。这个流程看着多花五分钟,实际上挡住过好几次返工。希望帮到你。
本文还有配套的精品资源,点击获取