# 2.1.shapefile读取

## 前言

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作为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`函数，其是一个只读访问权限的函数，因此是无法在读取数据的同时对数据进行更新或删除或插入等操作的，其定义如下：

```python
SearchCursor
 (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 前缀支持 **None**、**DISTINCT** 和 **TOP**。SQL 前缀支持 **None**、**ORDER BY** 和 **GROUP 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：

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

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

输出结果如下：

```bash
(116.06764901057997, 39.89192882186598) 32 北京
```

## 多点要素

核心代码：

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

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

## 线要素与多线要素

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

```python
with arcpy.da.SearchCursor(shp_file_path, ['Shape@', 'OID@']) as cursor:
    for row in cursor:
        print(row[1])
        geometry = row[0]
        print(u'part count: ' + str(geometry.partCount))
        part_num = 0
        for line in geometry:
            print('part num: ' + str(part_num))
            for pt in line:
                print('%.2f, %.2f' % (pt.X, pt.Y))
            part_num += 1
```

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

## 面要素与多面要素

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

```python
with arcpy.da.SearchCursor(shp_file_path, ['Shape@', 'OID@']) as cursor:
    for row in cursor:
        print(row[1])
        geometry = row[0]
        part_num = 0
        for line in geometry:
            print('part num: ' + str(part_num))
            for pt in line:
                if pt:
                    print('%.2f, %.2f' % (pt.X, pt.Y))
                else:
                    print('this is a hole')
            part_num += 1
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zxyao.gitbook.io/arcpy/2.1.shapefile-du-qu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
