ArcGIS arcpy代码工具——关于标识码的那些事(查找最大标识码、唯一性检查、重排序、空值赋值)

系列文章目录

ArcGIS arcpy代码工具——批量对MXD文件的页面布局设置修改
ArcGIS arcpy代码工具——数据驱动工具批量导出MXD文档并同步导出图片
ArcGIS arcpy代码工具——将要素属性表字段及要素截图插入word模板
ArcGIS arcpy代码工具——定制属性表字段输出表格
ArcGIS arcpy代码工具——批量栅格转点文件导出属性表



功能说明

关于标识码的那些事(查找最大标识码、唯一性检查、重排序、空值赋值)。

首先,认识一下标识码,标识码通常是指用于唯一标识某个实体(如人、物、组织、事件等)的一串字符,它可以是数字、字母或者是数字与字母的组合。在不同的领域中,名称标识码有着不同的应用和含义。类如身份证号码,唯一标识人的特征码。

在数据库中,标识码是一种用于唯一标识数据库对象的代码或值。它通常被用作主键或索引的基础。标识码可以是数字、字符或二进制数据,具体取决于数据库管理系统的实现。标识码可以通过自动生成、手动输入或使用特定算法来创建。在使用标识码时,需要注意确保其唯一性以避免数据冲突。

一般在地理数据库中,给图层的字段命名为BSM、标识码,字段类型有数值型或者字符型,是标识和定位地理要素的索引,故一般要求具有唯一性,不能重复,不能空值,维护起来较为复杂。

本文章针对维护标识码工作的痛点,介绍一些维护工具目标为:

  • 1 查找数据库中最大标识码
  • 2 标识码唯一性检查
  • 3 重排序标识码
  • 4 标识码空值赋值、更新补全

准备工作

实验工具,随便找一个地理数据库,可以是mdb或gdb格式,包含数据集图层和数据集外图层,每个图层含有BSM字段,字段类型有数值型,有字符型,个别图层缺失BSM字段,个别图层要素为空,个别图层BSM字段为空值null或者空格,个别图层的BSM值是重复值。
我使用的软件是10.8.2版本,文章中的代码和工具运行正常。

下面是样式数据的结构图:
1

1 查找最大标识码

一个合格的地理数据库,每个图层的每个要素都应该有一个唯一的标识码,那么这个地理数据库也应该有一个最大标识码,获取到最大标识码,针对地理数据库后续增加的要素就可以准确和方便的进行标识码的赋值工作。

如何查找地理数据库中的最大标识码?

思路如下:
遍历地理数据库中的要素集和单独图层,再遍历每个图层的中的每个要素,将标识码的值放入列表 [ ] 中,然后对列表取最大值,得到最大标识码。

再复杂一点,可以建立字典 { },针对每个图层进行查询,图层名作为字典的键,该图层的最大标识码作为字典的键值。这样就获知了每个图层的最大标识码,最后再对字典的键值取最大值,得到地理数据库的最大标识码值。

(1) 首先,判定字段是否存在

查找字段BSM的最大值,首先要判定字段是否缺失,如果缺失BSM字段,则后续的查找最大值也就可以跳过了。

关键代码 :
首先获取图层字段的集合,再使用 if 语句进行判定BSM字段是否在集合内,判定缺失则输出警告语句。

field_names = [f.name for f in arcpy.ListFields(feature_class)]
if field_name not in field_names:
	arcpy.AddWarning(xxxxx)

(2) 之后,判定图层是否为空

图层有BSM的字段,如果没有要素,空图层,后续查找最大值也可以跳过了。

关键代码 :
如何判定是否是空图层?可以这样理解,该图层的最大标识码为空值,即 None。如果为空,输出警告语句。

max_id = max(long(row[0]) for row in cursor)
max_id = None
arcpy.AddWarning(xxxxx)

(3) 查找最大值

图层有BSM字段,要素不为空,则可以查找最大值了。

关键代码 :
地理数据库,有要素数据集,有直接位于工作空间内的要素,在遍历的时候,要素数据集内的图层比外面的图层要多一层目录(即数据集的名称),这是成功遍历地理数据库所有要素层的关键。

//遍历所有要素集内的要素,如样例数据 T2021、T2022、T2025.
for fds in arcpy.ListDatasets("*", "Feature"):
    for fc in arcpy.ListFeatureClasses("*", "", fds):
    ......
    
//遍历直接位于工作空间内的要素,如样例数据的 T2023、T2024.
for fc in arcpy.ListFeatureClasses("*", "", ""):
	......

(4) 结果输出txt文档

地理数据库的图层可能很多,如果都使用arcpy.AddWarning()、arcpy.AddMessage()语句直接在运行窗口显示结果,无法仔细查看详细记录,故最好把结果输出到一个txt结果文档中保存,方便随时查阅。

关键代码 :
可以创建一个输出 txt文档的 函数,将查询过程中产生的 结果语句,调用函数写入txt文档。

def log_to_file(message, outtxt):
    with open(outtxt, "a") as txtfile:
        txtfile.write(message.encode('utf-8') + "\n")
        
def find_max_ids_in_workspace(a, b, output_file):
	......
	log_to_file(unicode(message, "utf-8"), output_file)

(5) 运行效果

我制作了arcgis工具箱工具 快速查找最大标识码BSM工具 ,有需要的可以去下载使用,下图是运行界面:

2

参数1:输入mdb和gdb地理数据库,支持中文目录;
参数2:指定查询的字段名称,可以是数值型字段和文本型字段,支持中文字段名;
参数3:输出结果txt,点击确定即可运行查找最大标识码。

我设置的地理数据库是某地类库,字段名称为BSM,运行结果如下图:
3
输出的 output.txt 保存的内容与运行窗口显示的一致,将运行结果完整的保存了下来。

2 标识码唯一性检查

标识码的作用就是标识和定位要素的唯一特征码,如果标识码重复了,那么地理数据库就是不合格的。所以,检查标识码的唯一性这项工作,就显得非常必要。

如何判定地理数据库中的标识码是否存在重复值?

思路如下:
遍历地理数据库中的要素集和单独图层,再遍历每个图层的中的每个要素,获取每个要素的标识码,在将 当前标识码 放入总列表 [ ] 前,判断该标识码值是否存在于 总列表 [ ] 中,如果在总列表中已经存在,则说明该标识码值存在重复,将该重复值存入单独的 重复值列表 [ ]。最后将重复的标识码结果输出txt文档进行记录。

再复杂一点,可以建立字典 { },标识码作为字典的键(因为重复的标识码可能存在于不同的图层中,而标识码值作为不同数字是唯一的),图层名字作为字典键值,当存入 重复值字典 { } 中,可以知道 重复的标识码 在 哪个图层中出现。

(1) 首先,判定字段是否存在、图层是否为空

首先要判定字段是否缺失,图层是否为空,满足条件后,开始后续的查找重复值。

(2) 判定标识码值是否重复

判定标识码是否重复,因为要挂上图层名称,处理起来要复杂一些。

首先建立总的标识码库 字典 all_values {},每个标识码值和图层名称组成字典键值对,存入总库;

再建立一个 比较过程的字典 temp_value {},因为要遍历每个图层中的每个要素,重复标识码可能存在于同一个图层内,也可能存在跨图层重复,过程字典 temp_value {} 就是存储单个图层的重复值情况;

每个图层的重复值 temp_value {} 再跟 总库 all_values {} 进行比较,找出跨图层的重复值情况,并记录图层名称,存入 最终的重复标识码库 duplicates {}

最后,使用 format 语句读取 重复标识码库 duplicates {} 输出重复标识记录,输出txt记录文档。

关键代码 :
情况1: 重复标识码存在于同一个图层内。

all_values = {} # 用于存储所有图层中该字段的所有值及其所在图层
temp_value = {} # 存储 同图层  重复值情况
......
for row in cursor:
	value = row[0]
    # 如果值已经在 all_values 中,说明重复
    if value in all_values:
    	# 更新temp_value,记录重复的图层名
    	if value not in temp_value:
        	temp_value[value] = [all_values[value], feature_class]
        else:
        	temp_value[value].append(feature_class)

情况2: 重复标识码存在于跨图层。

all_values = {} # 用于存储所有图层中该字段的所有值及其所在图层
duplicates = {}  # 存储最终发现的重复值及其所在图层列表
feature_class = 图层名称
......
for row in cursor:
	value = row[0]   
	if value in all_values:
		# 如果值已存在,则记录为重复
		duplicates.setdefault(value, set()).add(feature_class)                                
		duplicates[value].add(all_values[value])
	else:
		all_values[value] = feature_class

关键语句: duplicates.setdefault(value, set()).add(feature_class)

该函数首先通过 setdefault 方法,检查 value 是否存在于 duplicates字典 中,
如果存在,则直接返回对应的集合;
如果不存在,则在 duplicates字典 中创建一个以value为键的空集合,并将该集合返回。
接下来,通过调用集合的add方法,将feature_class图层名称添加到集合中。
这样可以确保同一个value所对应的集合中,不会出现重复的feature_class图层名称。
最后,该函数没有返回值,只是更新了 duplicates字典 中相应键值对应的集合。

(3) 结果输出txt文档

把结果输出到一个txt结果文档中保存,方便随时查阅。

(4) 运行效果

我制作了arcgis工具箱工具 标识码BSM唯一性检查工具,有需要的可以去下载使用,下图是运行界面:
4
参数1:输入mdb和gdb地理数据库,支持中文目录;
参数2:指定查询的字段名称,可以是数值型字段和文本型字段,支持中文字段名;
参数3:输出结果txt,点击确定即可运行查找最大标识码。

我设置的地理数据库是准备好的 test.mdb,其中存在重复的标识码,字段名称为BSM,运行结果如下图:
5
输出的 标识码唯一性检查结果.txt 保存的内容与运行窗口显示的一致,将运行结果完整的保存了下来。

3 标识码重排序

一个新建的地理数据库,在完成图形要素的编辑后,一般都是在最后一个步骤进行标识码重排序工作,即对地理数据库中的所有图层每个要素,安排一个唯一的不重复的标识码。一般要求同一个图层内要素的标识码是从小到大按增序排列的。

arcgis软件在保存地理要素的时候,要素图层有个默认字段 OBJECT ID字段,即常说的ID字段,通常由数据库系统自动分配,它的值是一个递增的整数序列,从1开始,每插入一条记录,其值就会自动加1,它是一种唯一标识符,用于标识数据库表中的每一行。这个字段可以作为空间数据表的主键,用于建立与其他表的关联。

arcgis软件默认会给每个要素一个不重复的 ID,但是如果删除了某个要素,则该要素的原始ID号在这个地理数据库中将消失不再重现,后续新建立的要素 ID会按照本图层最大ID续排。如果手工设置标识码,可以参考 这个ID字段值,但是难免发生中间断号的情况。

如何完成对整库标识码的重排序工作?

思路如下:
遍历地理数据库中的要素集,遍历要素集中的第1个图层,打开要素属性表,给每行属性表的BSM字段填写数值,从1开始往后增加,到该图层的最后一个要素结束,记录最后的标识码值,作为要素集第2个图层属性表中填写标识码的起始值。依此类推,遍历完要素集后,再遍历外部的图层,遍历完所有的图层后,完成标识码的完整重排序工作。

注意:思路中使用的方法是,调用要素属性表,按照要素属性表的顺序从上到下安排标识码,而属性表的顺序就是 ID字段 生成的顺序,所有本质上还是利用了 ID字段。

更高级的要素排序方法,还有按照地理要素的坐标值,进行从上到下,从左到右的方式进行排序,这种排序方式的好处是,临近的要素 顺序号较为接近或者连续,方便查找定位。

而我的文章中使用的方法,调用ID字段,虽然系统ID值也是按照要素产生的顺序先后生成的,但是不能保证周边的要素ID值是连续的。

(1) 首先,对数据库进行备份

涉及到对整库操作,对某个字段进行重新填写值,原有标识码值将被清除,为了数据安全,在标识码重排序前还是建立一个备份数据库为好。数据备份工作也是 一个成熟GISer的良好习惯。

备份库命名就以时间戳命名,因为地理数据库有文件地理数据库(.gdb)和个人地理数据库(.mdb)
两种格式,后缀名获取自 参数1数据库名称即可。

关键代码 :

def create_backup(workspace, backup_path):
    timestamp = datetime.now().strftime("%Y%m%d%H%M")  # 获取当前时间戳
    mdborgdb = os.path.splitext(workspace)[1]  # 更加可靠地获取数据库扩展名
    backup_name = "备份库_{}{}".format(timestamp, mdborgdb)  # 创建备份文件名
    backup_full_path = os.path.join(backup_path, backup_name)  # 构建完整备份路径

	arcpy.Copy_management(workspace, backup_full_path, data_type="Workspace")

(2) 判定字段是否存在

首先要判定字段是否缺失,字段缺失将跳过该图层。满足条件后,开始后续的查找重复值。

(3) 默认起始值

这里增加了一个参数:起始值,默认起始值为1,即从1开始续排。
如果有特殊要求,比如说为了区分数据库,第一个数据库要求标识码从1开始最大999,第二个数据库要求标识码从1000开始最大1999,这个起始值可以设定。

(4) 重排标识码

从设定的起始值start_value开始重排标识码,第一个图层结束后,会产生一个临时最大值,作为下一个图层的起始值,所以 start_value值是一直在变化的。

遍历并增加过程值,最好的方式是 枚举,使用start_value作为计数器的起始值。
关键代码 :

with arcpy.da.UpdateCursor(feature_layer, bsm_field_name) as cursor:
	for i, row in enumerate(cursor, start=start_value):
		row[0] = i
		cursor.updateRow(row)
		......
		i += 1
		......

(5) 记录最大值

在重排序函数的外面,建立一个全局参数 max_id = [],将过程最大标识码加入 列表中。最后对列表求取最大值,使用输出语句进行汇报:重排序后最大值为xxx。

关键代码 :

 global max_id = []  # 初始化列表用于存储每个要素类的最大值
		......
		max_id.append(i)  # 假设i是当前要素类的最大排序值
		......
max_value = max(max_id)
arcpy.AddMessage("重排序完成,最大值为 {} .".format(max_value))

(6) 运行效果

我制作了arcgis工具箱工具 数据库标识码BSM重排序工具、重构标识码工具,有需要的可以去下载使用,下图是运行界面:

6
参数1:输入mdb和gdb地理数据库,支持中文目录;
参数2:指定查询的字段名称,可以是数值型字段和文本型字段,支持中文字段名;
参数3:重排序起始值,默认从1开始重排,也可以修改重排的起始值如1000、2000等,以2000、2001、2002排序;
参数4:备份库保存路径,这里选择路径文件夹即可,工具会按照时间戳给备份库命名并复制备份库。

我设置的地理数据库是某地类库,重排字段名称为BSM,重排起始值为1,备份库保存路径为 “D:\临时” ,运行结果如下图:
7

8

4 更新补全字段、空值赋值

在编辑地理数据库的时候,新增的要素往往来不及给标识码赋值,结果产生一些要素的BSM字段为空值,或者对某些选中的要素进行标识清空操作。或者是某些字段需要统一赋相同值,如要素代码。

如何实现这两个功能?

思路如下:
工具设定的是给空值字段赋值,可以是空值null,也可以是填充的空格,所以在使用工具前,需要思考自己想要实现的操作。
工具仅对空值或者空格值的要素进行操作,所以数据安全有保障。

情况1: 统一赋值,即对某个字段填写相同的值,前提条件是该字段为空,否则只填写那些字段为空值的要素。可以使用字段计算器 “字段 = null” 对字段提前清空,经过这种深思熟虑思考的操作后,也就免除了数据备份工作,代表着这一步骤并不是误操作。

情况2: 更新补全字段,针对空值的要素,有一个起始值,从起始值开始续排字段。这就要提前知道该地理数据库中现有的最大值标识码,可以使用我编写的工具 快速查找最大标识码BSM工具 查询出最大标识码,作为补全操作的起始值。

(1) 判定字段是否存在

首先要判定字段是否缺失,字段缺失将跳过该图层。满足条件后,开始后续的补全操作。

(2) 起始值(仅可以填写整数)

对空值字段操作的起始值,如果是统一赋值则表示选定字段都填写 该起始值。
如果是补全字段,则从起始值(含)开始每次+1续排。
因为要进行 加法计算,所以 起始值只能填写整数,即全都是数字组成的,纯数字组成的字符型也可以。

(3) 选择递增值,工具判定统一赋值操作还是补全字段操作

每次递增值,有 0 、 1 两个选项:
选择递增值为 0,表示进行统一赋值操作,对字段统一赋值为起始值;
选择递增值为 1,表示补全字段操作,从起始值(含)进行 +1 续排。

关键代码 :

my_workspace = 数据库
field_name = 字段名称
starting_value = 起始值
incremental_value = 递增值
countvalue = 0  # 更新要素的计数,更新了多少个

def updpdateFc(my_workspace, field_name, starting_value, incremental_value, countvalue=0):
......
	for row in cursor:
		if row[0] is None or row[0] == "" or row[0] == " " or row[0] == "  " or row[0] == "   ": #找到空值 或 空格值
			row[0] = starting_value
			cursor.updateRow(row)
			if incremental_value != 0:    # 判定,如果递增值 不是 0,表示补全更新,starting_value +1 续排。
				starting_value = starting_value + incremental_value   # 过程最大值
			countvalue += 1                              # 更新要素的计数
			......

(4) 根据两种不同操作,输出不同结果语句

使用 if 判定,输出两种不同的结果语句。

关键代码 :

if incrementalvalue != 0:
	starting_value, countvalue = updpdateFc(myworkspace, fieldname, int(startingvalue), int(incrementalvalue))
	arcpy.AddMessage("更新起始值 " + str(startingvalue) + ",终了最大标识码 " + str(starting_value-1) + ",更新数量 " + str(countvalue) + "。")
else:
	starting_value, countvalue = updpdateFc(myworkspace, fieldname, startingvalue, incrementalvalue)
	arcpy.AddMessage("空值字段统一赋值成功,赋值数量 " + str(countvalue) + "。")

(5) 运行效果

我制作了arcgis工具箱工具 更新补全字段BSM工具,空值字段赋值工具,有需要的可以去下载使用,下图是运行界面:

9

参数1:输入mdb和gdb地理数据库,支持中文目录;
参数2:指定查询的字段名称,可以是数值型字段和文本型字段,支持中文字段名;
参数3:起始值,仅可以输入整数;
参数4:每次递增值,选择 0 表示统一赋值为 起始值; 选择 1 表示 从起始值(含) +1 续排。

我设置的地理数据库是准备好的 test.mdb,字段名称为BSM,起始值为2144477777,递增值选择 0 ,即 统一赋值操作,运行结果如下图:
10
递增值选择 1 ,即 更细补全字段操作,运行结果如下图:

11

5 后记

地理数据库的标识码BSM,是日常工作中经常需要维护的一个工作,尤其有些数据库更新工作,去年的旧库作为今年的基础库,这种库的标识码一般情况下是不允许重排的。这种情况下,就需要掌握 旧库的标识码信息,如最大值等等,方便后续工作。网络上也有类似的标识码更新工具,只有一个运行操作,结果不透明,不知其所以然。

我就自己编写了这几个工具,主要是能够输出记录,方便历史查阅。这些工具也可以用于给字段赋值,兼容数值型字段和字符型字段(纯数字组成),支持中文路径,支持mdb和gdb格式地理数据库。

编写arcgis工具的过程,也练习了arcpy的编程,这也是一个学习的过程,大家多多支持,多收藏多点赞,一起共勉之。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

da-peng-song

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值