统计
  • 文章总数:855 篇
  • 评论总数:0 条
  • 分类总数:14 个
  • 最后更新:8月3日

ArcGIS二次开发(五):你不知道的属性表游标本质和应用

本文阅读 12 分钟
首页 ArcGIS 正文

游标对象是什么

在 ArcPy 中检索、更新、修改矢量文件属性表是非常常见的操作。而 ArcPy 也提供了3个专门的方法来实现这些功能。

首先我们需要了解 ArcPy 中的数据访问模块:da(arcpy) 模块,英文:The data access module,是一个数据处理模块。

而就在该模块中有三种游标方法,使用这三种方法可以创建三种不同的游标对象(实例),我们进一步使用游标对象就可以获取、修改、插入属性表中的各种数据。

这三个方法首次是在 ArcGIS10.1 版本中加入:arcpy.da.SearchCursorarcpy.da.UpdateCursorarcpy.da.InsertCursor

需要注意的是,在 ArcGIS10.1 版本前大家使用的是三个旧的游标方法:arcpy.SearchCursorarcpy.UpdateCursorarcpy.InsertCursor

新版的游标比旧版的性能更好,但是为了兼容先前的代码,旧的方法没有被移除,只是不再推荐使用了。也就是说你可以在 ArcGIS10.1 之后的 ArcPy 中同时使用新版游标方法和旧版游标方法。

但是由于新版游标的性能更好,另外新旧游标对象各自的方法都有些许差异,所以没有必要去研究和使用旧版游标对象了。

 

游标对象是什么?那么现在回答这个问题:游标对象就是通过执行游标方法而获得的游标对象实例,使用游标对象可以获取属性表数据(包括几何属性)

 

  • 速度比较图表来源:https://gis.stackexchange.com/questions/108807/how-is-the-data-access-cursor-performance-so-enhanced-compared-to-previous-versi

 

 

Note:后文提及的游标及其方法均指新版游标和新版方法,请注意。

 

 

 

三种游标对象

三种游标对象的使用方法在官方文档里写的很清楚,所以我简单复制一下。除开官方文档外,我会重点讲一下注意事项和游标的本质及其应用

 

下面是用于演示的矢量文件的初始属性表。

 

 

../NYC.gdb/Boroughs 要素的初始属性表

 

 

1.SearchCursor

搜索游标,当然你想叫什么样的中文名都可以,该游标是只读游标,只能查询属性表中信息,不能修改。

创建该游标的语法如下:

 

cursor = arcpy.da.SearchCursor (in_table, field_names, 
    {where_clause}, {spatial_reference}, {explode_to_points},{sql_clause})

 

该方法前2个如 in_tablefield_names 是必选参数,后面4个是可选参数(很少使用到)。

第一个参数输入要素类、图层、表或表视图;第二个参数输入字段名称或者令牌,虽然令牌这个说法可能看起来比较怪,其主要用于获取属性表中没有的信息,比如几何的质心、周长、面积等,使用起来也非常简单,和字段名称输入方式一样,加上引号当字符串传进去就好,具体有那些种类的令牌官方文档上写的很清楚。

 

1.1属性

fields:返回字段名称参数(几乎根本不会使用)。

 

1.2方法

next ():将下一行作为元组返回。字段将按照创建光标时所指定的顺序返回。

reset ():将光标重置回第一行。

 

1.3示例代码

示例代码可见文件 ../Chapter5/1.SearchCursor.py

 

# -*- coding:utf-8 -*-

import arcpy
import os

arcpy.env.overwriteOutput = True
wk_path = os.path.abspath("../NYC.gdb")
arcpy.env.workspace = wk_path

field = ["BoroName", "SHAPE@AREA"] # ▶注释1◀
with arcpy.da.SearchCursor("Boroughs", field) as cursor:
print cursor.fields
print "Next 1: {}".format(cursor.next()) # ▶注释2◀
print "Next 2: {}".format(cursor.next())
print "Next 3: {}".format(cursor.next())
print "Next 4: {}".format(cursor.next())
print "Next 5: {}".format(cursor.next())
cursor.reset() # ▶注释3◀
print "Next 1: {}".format(cursor.next())
print "Next 2: {}".format(cursor.next())
print "Next 3: {}".format(cursor.next())
print "Next 4: {}".format(cursor.next())
print "Next 5: {}".format(next(cursor)) # ▶注释4◀

 

输出结果:

 

(u'BoroName', u'SHAPE@AREA')
Next 1: (u'Staten Island', 1601777454.0332422)
Next 2: (u'Brooklyn', 1956932802.231596)
Next 3: (u'Queens', 3030316015.6541715)
Next 4: (u'Bronx', 1171019852.6289911)
Next 5: (u'Manhattan', 630673101.7271129)
Next 1: (u'Staten Island', 1601777454.0332422)
Next 2: (u'Brooklyn', 1956932802.231596)
Next 3: (u'Queens', 3030316015.6541715)
Next 4: (u'Bronx', 1171019852.6289911)
Next 5: (u'Manhattan', 630673101.7271129)

 

▶注释1◀:使用了令牌 SHAPE@AREA,该令牌可以获取每个要素的面积。

▶注释2◀:使用游标内置的 next() 方法可以依次遍历属性表内容。

▶注释3◀:reset() 重置游标位置,如果再继续使用 next() 方法的话就会报错,因为从上面可以看到Boroughs 属性表中只有5行数据,现在游标已经到了属性表末尾。

▶注释4◀:这里使用了 next(cursor),效果和 cursor.next() 一样,next(obj) 是 Python 的内置方法。具体详情可见下面的游标的本质小结。

 

  • 官方在线文档:https://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy-data-access/searchcursor-class.htm

 

2.UpdateCursor

更新游标:该游标是我使用最为频繁的游标,它不仅能遍历属性表,替代搜索(SearchCursor)游标,还能够更新属性表内容。

创建该游标的语法如下:

 

cursor = arcpy.da.UpdateCursor (in_table, field_names, 
        {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})

 

该游标方法的参数和搜索游标的方法一致,前2个如 in_tablefield_names 是必选参数,后面4个是可选参数(很少使用到)。

 

2.1属性

fields:返回字段名称参数(同样几乎不会使用)。

 

2.2方法

next ():将下一行作为元组返回。字段将按照创建光标时所指定的顺序返回。

reset ():将光标重置回第一行。

除开这两个搜索游标都有的方法外,更新游标有两个自带的独特方法:

deleteRow ():删除当前行。

updateRow (row):更新表中的当前行

 

2.3示例代码

示例代码可见文件 ../Chapter5/2.UpdateCursor.py

 

# -*- coding:utf-8 -*-

import arcpy
import os

arcpy.env.overwriteOutput = True
wk_path = os.path.abspath("../NYC.gdb")
arcpy.env.workspace = wk_path

field = ["BoroName", "SHAPE@AREA"]
with arcpy.da.UpdateCursor("Boroughs", field) as cursor:
for row in cursor:
row[0] = row[0] + "_1" # ▶注释1◀

row[0] = row0

cursor.updateRow(row) # ▶注释2◀

 

输出结果:

 

  • 官方在线文档:https://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy-data-access/updatecursor-class.htm

 

3.InsertCursor

插入游标:该游标与以上两个游标差别较大。使用该游标来添加新行。

创建该游标的语法如下:

 

cursor = arcpy.da.InsertCursor (in_table, field_names)

 

参数只有两个必选参数。

 

3.1属性

fields:返回字段名称参数(同样几乎不会使用)。

 

3.2方法

insertRow (row):向表中插入一行。

 

3.3示例代码

示例代码可见文件 ../Chapter5/3.InsertCursor.py

 

# -*- coding:utf-8 -*-

import arcpy
import os

arcpy.env.overwriteOutput = True
wk_path = os.path.abspath("../NYC.gdb")
arcpy.env.workspace = wk_path

field = ["BoroName", "SHAPE@"]
cursor = arcpy.da.InsertCursor("Boroughs", field)

array = arcpy.Array([arcpy.Point(993701.189, 232208.219),
arcpy.Point(976940.744, 245402.664),
arcpy.Point(965482.411, 211027.664),
arcpy.Point(974162.966, 191583.219)])
new_polygone = arcpy.Polygon(array) # ▶注释1◀

cursor.insertRow(["test", new_polygone]) # ▶注释2◀
cursor.insertRow(["test2", None]) # ▶注释3◀
del cursor

 

▶注释1◀:创建了一个几何对象,面 new_polygone;

▶注释2◀:使用 insertRow 方法新添加了一行,同时附带了几何对象,输出的结果如下:

 

 

新增加了一个几何(高亮部分)

 

 

▶注释3◀:不用几何对象也能添加新的一行,不过这显然就违背了矢量数据的基本要求,即每条属性对应一个几何形状(对象)。

如下图所示,黄色框中的数据行是 ▶注释2◀ 中新增几何形状对应的属性;而紫色框中的数据行则是纯粹的属性,没有任何对应的几何形状。

当然不建议新增没有对应几何的数据行,没有意义,这里只是用于演示。

 

 

运行示例代码后,属性表新增了两行

 

 

  • 官方在线文档:https://desktop.arcgis.com/zh-cn/arcmap/latest/analyze/arcpy-data-access/insertcursor-class.htm  

 

 

3.游标的本质

3.1本质

好的,众所周知的三种游标终于说完了,现在说说很多人不知道的东西,也是这篇文章的精华所在:游标的本质

游标的本质是什么?语言是苍白的,直接上代码:

 

示例代码可见文件../Chapter5/4.cursor.py

# -*- coding:utf-8 -*-

import arcpy
import os
from collections import Iterable, Iterator, Sequence

arcpy.env.overwriteOutput = True
wk_path = os.path.abspath("../NYC.gdb")
arcpy.env.workspace = wk_path

l = [1, 2, 3, 4]
cursor = arcpy.da.SearchCursor("Boroughs", ["OID@", "SHAPE@"])

abcs = (Iterable, Iterator, Sequence)
print [isinstance(l, abc) for abc in abcs]

[True, False, True] #Iterable, not Iterator, Sequence

print [isinstance(cursor, abc) for abc in abcs] # ▶注释1◀

[True, True, False] #Iterable, Iterator, not Sequence

使用内置函数 iter() # ▶注释2◀

it_l = iter(l)
t1 = type(l) # <type 'list'>
t2 = type(it_l) # <type 'listiterator'>

it_cur = iter(cursor)
t3 = type(cursor) # <type 'da.SearchCursor'>
t4 = type(it_cur) # <type 'da.SearchCursor'>

look at identify of cursor objects

print id(cursor) # 105692320
print id(it_cur) # 105692320

 

从 ▶注释1◀ 可以看到游标是可迭代对象、也是迭代器、不是序列。

包括后面的 ▶注释2◀ 也说明了游标是一种迭代器:迭代是数据处理的基石,而按照惰性获取数据项的方式,即按需一次获取一个数据项,这叫迭代器模式(Iterator pattern),而迭代器就负责实现这一模式功能。

 

所以游标拥有迭代器的各种特性,在这个基础之上,我们可以最大化的利用 Python 的优势,写出优美简洁的代码。

 

3.2本质的应用

灵活运用迭代器能省下大量的功夫,这里使用一些实例来说明。以下所有示例代码见文件 ../Chapter5/5.sample.py

 

拆包

元组拆包就是迭代器支持的功能之一。可以简化代码,见下面示例代码的 ▶注释1◀ 。

 

# ...
field = ["BoroName", "SHAPE@AREA"]
with arcpy.da.SearchCursor("Boroughs", field) as cursor:
  for name, area in cursor: # ▶注释1◀
    print name

 

惰性释放

迭代器是惰性:迭代器不会一次性把所有数据拿到“手上”,而是根据需要,一个一个的拿数据。但是如果我们想要一次性获取所有数据呢?难道要老实的遍历一次,把每一次的数据都装进一个列表?

当然不必这样,使用 python 内置函数 list(),可以直接释放惰性。

 

# 惰性释放

field = ["BoroName", "SHAPE@AREA"]

<<<传统写法>>> ▶注释1◀

new_list = []
with arcpy.da.SearchCursor("Boroughs", field) as cursor:
for name, area in cursor:
new_list.append((name, area))

将全部数据装进一个列表

print new_list
del cursor

 <<<<惰性释放写法>>> ▶注释2◀

cursor = arcpy.da.SearchCursor("Boroughs", field)
new_list2 = list(cursor)
print new_list2
del cursor

 

▶注释1◀ 这里这是普通的写法,需要先创建一个容器(列表),然后一个一个把内容装进去。而 ▶注释2◀ 位置就是直接使用内置的列表构造函数 list(),直接去除惰性,因为一个列表是完全没有惰性的。

当然特定情况下可能需要去除惰性,比如结合 len() 函数快速掌握这个矢量数据有多少个几何形状(属性)。

 

获取极值

如何获取某一字段的最大值或者最小值是多少呢?一条属性一条属性的遍历然后比较大小嘛?还是使用 ArcGIS 自带的表达式?

其实都不用,一行代码解决问题,

 

# 获取极值

from operator import itemgetter
field = ["BoroName", "SHAPE@AREA"]
with arcpy.da.SearchCursor("Boroughs", field) as cursor:
print max(cursor, key=itemgetter(1))

 

输出结果如下,面积最大的是“皇后区”:

 

(u'Queens', 3030316015.6541715)

 

降序/升序排列

通过一个字段值的大小进行降序/升序排列。如果不结合迭代器自身的特性你要怎样实现呢?

但是其实也是只需一两行代码就能轻松实现。

 

from operator import itemgetter
field = ["BoroName", "SHAPE@AREA"]
with arcpy.da.SearchCursor("Boroughs", field) as cursor:
  for name, area in sorted(cursor, key=itemgetter(1), reverse=True):
    print (name, area)

 

输出结果:

 

(u'Queens', 3030316015.6541715)
(u'Brooklyn', 1956932802.231596)
(u'Staten Island', 1601777454.0332422)
(u'Bronx', 1171019852.6289911)
(u'Manhattan', 630673101.7271129)

 

最后

为什么 Python 这么流行的原因就是简单、优美,是 Pythonic。

在这里我们不局限于 ArcPy 这一个站点包,视野开阔到整个 Python 语言,在了解了游标的本质后我们能轻车熟路的使用很多 Python 内置的函数或者默认库写出优美、简洁的代码,不仅仅局限于上面简单提到的几个案例。

 

 

结束语

看完本章节,你可以知道:

  1. 三种游标及其对应创建方法;
  2. 三种游标的使用方法和区别;
  3. 游标的本质是迭代器;
  4. 结合迭代器的特性写出优美的代码;
  5. 希望你跳出 ArcPy 的局限,将视野放大到 Python 上。

 

使用版本:

  • Windows 10
  • PyCharm 2020.3.3
  • ArcGIS 10.3
  • Python 2.7.8

 

本文来自投稿,不代表本站立场,如若转载,请注明出处:
用Python创建你第一个GIS程序[1]:地理空间处理库的选择
« 上一篇 03-12
ArcGIS中点线面要素在栅格中的表示方式
下一篇 » 03-12

作者信息

作者有点忙,还没写简介
TA的最新作品
    请设置要调用的作者ID

动态快讯

    请配置好页面缩略名选项

热门文章

标签TAG