前言

shapefile作为Esri提出的一种地理数据存储格式,是ArcGIS最主要的数据源格式格式之一,ArcPy必然提供了对其读写的支持。

通常,在大部分的地理空间数据处理工具、类库以及一些协议中,都将一个地理实体对象抽象为一个Feature(要素),包括ArcGIS、DotSpatial、Openlayers、GeoJSON等,当然ArcPy也是这样的定义。多个Feature的集合称之为要素类、要素集合(简称要素集)等(FeatureSet,FeatureCollection等),在ArcGIS中,其一般称之为要素类,而多个要素类集合称为要素数据集(简称要素集)。在此声明,本文中若没有特殊声明,所有的要素数据集或要素集都是代表要素类、要素集合

Shapefile的访问是通过使用arcpy.da模块实现的,arcpy.da模块为arcpy的数据访问(data access)模块。数据访问时使用游标来指示数据的位置,在数据读取时只能单向向后访问数据,而无法从文件中再次获取已经已经访问过的数据,或者是通过reset()方法从头开始访问。同时,arcpy中的数据读取与增加、更新(包括数据修改与删除操作)是分离的,也就是说在读取数据的同时,是无法对数据进行数据的更新或添加。

函数介绍

‌读取shapefile使用的是arcpy.da.SearchCursor函数,其是一个只读访问权限的函数,因此是无法在读取数据的同时对数据进行更新或删除或插入等操作的,其定义如下:

  1. SearchCursor
  2. (in_table, field_names, {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})

参数的意义如下:

参数 说明 数据类型
in_table 要进行操作的数据源路径,包括要素类、shapefile 或表。 String
field_names 字段名称列表(或组)。对于单个字段,可以使用一个字符串,而不使用字符串列表。如果要访问输入表中的所有字段(栅格和 BLOB 字段除外),可以使用星号 (*) 代替字段列表。如果需要访问数据的空间信息,需要通过SHAPE@的形式(其形式与意义见表下)指定,如果是属性信息,直接填写其字段名即可。但是,为了获得较快的性能和可靠的字段顺序,建议通过字段列表限制在实际需要的字段。不支持栅格字段。 String
where_clause 对读取的数据进行筛选的条件,为SQL的where字句中的内容,例如:name=’武汉’ String
spatial_reference 要素类的空间参考。可以使用SpatialReference对象或等效字符串来指定。指定后,要素将使用提供的 spatial_reference 进行动态投影。例如,如果原数据仅有地理坐标系,可以通过设置投影坐标系获取要素在投影坐标系中的坐标。 SpatialReference
explode_to_points 是否将要素解构为单个点或折点。例如,将 explode_to_points 设置为 True,则一个包含五个点的多点要素将表示为五行。 Boolean
sql_clause 以列表或组的形式列出的可选 SQL 前缀和后缀子句对。SQL 前缀支持 NoneDISTINCTTOP。SQL 前缀支持 NoneORDER BYGROUP BY。SQL 后缀子句位于第二个位置,将追加到 SELECT 语句的 where 子句之后。SQL 后缀子句最常用于 ORDER BY 等子句。例如:(‘top 3’, ‘order by field1 desc’),表示根据字段field1降序排序,并取前3个。注意:仅在使用数据库时支持使用 DISTINCT、ORDER BY 和 ALL。它们不受其他数据源(如 dBASE 或 INFO 表)的支持。 tuple
  • SHAPE@XY —一组要素的质心 x,y 坐标。
  • SHAPE@TRUECENTROID —一组要素的真正质心 x,y 坐标。
  • SHAPE@X —要素的双精度 x 坐标。
  • SHAPE@Y —要素的双精度 y 坐标。
  • SHAPE@Z —要素的双精度 z 坐标。
  • SHAPE@M —要素的双精度 m 值。
  • SHAPE@JSON — 表示几何的 esri JSON 字符串。
  • SHAPE@WKB —OGC 几何的熟知二进制 (WKB) 制图表达。该存储类型将几何值表示为不间断的字节流形式。
  • SHAPE@WKT —OGC 几何的熟知文本 (WKT) 制图表达。其将几何值表示为文本字符串。
  • SHAPE@ —要素的几何对象。
  • SHAPE@AREA —要素的双精度面积。
  • SHAPE@LENGTH —要素的双精度长度。
  • OID@ —字段ObjectID 或FID的值。

:SHAPE@JSON、SHAPE@WKB 和 SHAPE@WKT 令牌在 ArcGIS 10.1 Service Pack 1 中可用。

引用自_:https://desktop.arcgis.com/zh-cn/arcmap/10.3/analyze/arcpy-data-access/searchcursor-class.htm

arcpy.da.SearchCursor具有两个方法:next()reset(),我们一般不会手动调用next(),因为它是进行数据迭代的接口,而reset()方式是用来将游标重置到第一行数据时的。

点要素读取

代码Demo:

  1. shp_file_path = ur'../data/ReadData/中国省会城市.shp'
  2. # 这里有个取巧,因为此要素数据集为点要素,对于点要素,质心坐标就是点坐标
  3. fields_array = ['SHAPE@XY', 'id', '城市名']
  4. # 定义WGS84坐标系
  5. spatial_reference = arcpy.SpatialReference(4326)
  6. with arcpy.da.SearchCursor(shp_file_path, fields_array, "城市名='北京'", spatial_reference) as cursor:
  7. for row in cursor:
  8. for data in row:
  9. print(data),
  10. print('\n')

这段代码是从“中国省会城市.shp”的shapefile文件中,筛选读取名为北京的要素,读取的字段为质心、id和城市名。由于原来数据使用的是投影坐标系,现在将其变换为WGS84坐标系,这样读取的质心的坐标就是以经纬度为单位的。

输出结果如下:

  1. (116.06764901057997, 39.89192882186598) 32 北京

多点要素

核心代码:

  1. with arcpy.da.SearchCursor(shp_file_path, ['Shape@', 'OID@']) as cursor:
  2. for row in cursor:
  3. print(row[1])
  4. geometry = row[0]
  5. for pt in geometry:
  6. print('%.2f, %.2f' % (pt.X, pt.Y))

多点要素在读取空间信息时,通过空间信息的部分进行遍历,即可获取每个多点要素内部各个点的空间坐标。

线要素与多线要素

在ArcGIS的数据模型里,线要素与多线要素都是线要素类,因此,你可以在一个线图层中同时创建线要素与多线要素。因此在读取数据的时候需要判断一个要素是不是多部件要素,核心代码如下:

  1. with arcpy.da.SearchCursor(shp_file_path, ['Shape@', 'OID@']) as cursor:
  2. for row in cursor:
  3. print(row[1])
  4. geometry = row[0]
  5. print(u'part count: ' + str(geometry.partCount))
  6. part_num = 0
  7. for line in geometry:
  8. print('part num: ' + str(part_num))
  9. for pt in line:
  10. print('%.2f, %.2f' % (pt.X, pt.Y))
  11. part_num += 1

可以看到这里与读取多点要素时相似的,只是在遍历geometry的内部又进行了一次遍历。

面要素与多面要素

在ArcGIS的数据模型里,面要素与多面要素也都是面要素类,因此,你可以在一个面图层中同时创建面要素与多面要素。因此在读取数据的时候需要判断一个要素是不是多部件要素,核心代码如下:

  1. with arcpy.da.SearchCursor(shp_file_path, ['Shape@', 'OID@']) as cursor:
  2. for row in cursor:
  3. print(row[1])
  4. geometry = row[0]
  5. part_num = 0
  6. for line in geometry:
  7. print('part num: ' + str(part_num))
  8. for pt in line:
  9. if pt:
  10. print('%.2f, %.2f' % (pt.X, pt.Y))
  11. else:
  12. print('this is a hole')
  13. part_num += 1

可以看到其与线要素坐标信息的读取时相似的,不同的是在内部对边进行遍历输出点的坐标时,使用if语句进行了一次判定,这是因为一个线部件可能还包含有内环,每个内环之间使用的None对象进行分隔,如果为None,那么接下来将会输出内环上的点坐标。

数据的更新

更新shapefile使用的是arcpy.da.UpdateCursor函数,UpdateCursor 能够对游标所访问的数据进行读写,其定义如下:

  1. UpdateCursor(in_table, field_names, {where_clause}, {spatial_reference}, {explode_to_points}, {sql_clause})

参数的意义与arcpy.da.SearchCursor相同。

UpdateCursor具有四个方法:

deleteRow():删除当前要素 next():用于迭代时获取下一行数据 reset():将游标重置到第一行 updateRow(row):更新当前行数据

接下来来看一段代码:

  1. shp_file_path = ur'../data/ReadData/Point.shp'
  2. fields_array = ['SHAPE@', 'Id']
  3. # 定义WGS84坐标系
  4. spatial_reference = arcpy.SpatialReference(4326)
  5. with arcpy.da.UpdateCursor(shp_file_path, fields_array, spatial_reference=spatial_reference) as cursor:
  6. row_index = 0
  7. for row in cursor:
  8. if row_index == 0:
  9. new_id = random.randint(1, 100)
  10. row[1] = new_id
  11. else:
  12. new_y = random.randint(-90, 90)
  13. old_pt = row[0].firstPoint
  14. row[0] = arcpy.Point(old_pt.X, new_y)
  15. row_index += 1
  16. cursor.updateRow(row)

这段代码是更新了一个“Point.shp”文件。开始,我们定义了需要搜索的字段为[‘SHAPE@’, ‘Id’],当row_index == 0时,更改了属性中的第2个字段——Id——为一个随机整数;当row_index == 1时,更改了属性中的第1个字段——几何信息,此处为点——的y坐标为一个在[-90,90]之间的随机整数。