>>> from env_helper import info; info()
页面更新时间: 2024-03-04 14:28:04
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-18-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

5.4. 高级主题

5.4.1. 切片和masking迭代器

若想添加空间矢量数据格式,可查找外包四边形(bounding box) 。 函数 fiona.collection.Collection.items 返回一个FIDs迭代器, 并记录交叉指定的 (minx, miny, maxx, maxy) bounding box或几何对象。 自己的坐标参考系统可用来解译box值。 如果要查看迭代器列表,可通过 Python 的内置 list() 函数 ,如下所示。

>>> import fiona
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> hits = list(c.items(bbox=(-5.0, 55.0, 0.0, 60.0)))
>>> len(hits)
0

迭代器与切割参数itertools.islicestopstart, stop[, step] 的功能相同。要想查找迭代器的前两个参数,暂停即可。

>>> hits = c.items(2, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
0

要想得到迭代器第三到第五个参数,启动和停止即可。

>>> hits = c.items(2, 5, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
0

要想过滤属性值,可使用Python的内置 :py:func:filter 和:py:keyword:lambda函数 ,或者你也可以用单一属性的过滤功能,返回值为 True or False

>>> def pass_positive_area(rec):
>>>     return rec['properties'].get('AREA', 0.0)>0.0
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> hits = filter(pass_positive_area, c)
>>> len(list(hits))
0

5.4.2. 多层数据的处理

读取多层数据

一般GIS数据格式可以在单个文件或库中编码多个层或特征类型,而ESRI Shapefile只有一个主题层或功能型的单一dataset。ESRI文件地理数据库 <http://www.gdal.org/ogr/drv_filegdb.html> 就是一个例子,它是一个包含多个shapefiles的库。以下命令是Fiona测试数据中创建的二层数据源。

>>> import subprocess
>>> import os
>>> if not os.path.exists('/tmp/data'):
>>>     os.mkdir('/tmp/data')
>>>
>>> cmd1 = 'ogr2ogr /tmp/data/ /gdata/GSHHS_c.shp GSHHS_c -nln foo'
>>> cmd2 = 'ogr2ogr /tmp/data/ /gdata/GSHHS_c.shp GSHHS_c -nln bar'
>>> subprocess.call(cmd1, shell=True)
>>> subprocess.call(cmd2, shell=True)
Warning 1: Value 50654050.6944999993 of field area of feature 0 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 50654050.6944999993 of field area of feature 1 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 29220969.7270000018 of field area of feature 2 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 20154740.0899999999 of field area of feature 3 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 17534164.0667999983 of field area of feature 4 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 13919140.5654000007 of field area of feature 1736 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 50654050.6944999993 of field area of feature 0 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 50654050.6944999993 of field area of feature 1 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 29220969.7270000018 of field area of feature 2 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 20154740.0899999999 of field area of feature 3 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 17534164.0667999983 of field area of feature 4 not successfully written. Possibly due to too larger number with respect to field width
Warning 1: Value 13919140.5654000007 of field area of feature 1736 not successfully written. Possibly due to too larger number with respect to field width
0

一个数据源层可以用函数fiona.listlayers 列一下,写法是 listlayers()。在 Shapefile格式的情况下,层的名称需匹配文件库的名称。

>>> import fiona
>>> fiona.listlayers('/gdata')
['GSHHS_h',
 'hyd2_4l',
 'shape_towns',
 'county_popu',
 'GSHHS_c',
 'prov_capital',
 'world_pt',
 'region_popu',
 'world_spatial_filter',
 'region_popu2',
 'spatial_filter',
 'GSHHS_l_L1']

不像OGR,Fiona没有层与数据源的分类。要想访问层,需用数据源路径打开一个集,并用 layer 来指定该层。

>>> import pprint
>>> datasrc_path = '/gdata'
>>> for name in fiona.listlayers(datasrc_path):
>>>     with fiona.open(datasrc_path, layer=name) as c:
>>>         pprint.pprint(c.schema)
{'geometry': 'Polygon',
 'properties': OrderedDict([('id', 'str:80'),
                            ('level', 'int:10'),
                            ('source', 'str:80'),
                            ('parent_id', 'int:10'),
                            ('sibling_id', 'int:10'),
                            ('area', 'float:19.11'),
                            ('Shape_Leng', 'float:19.11'),
                            ('Shape_Area', 'float:19.11')])}
{'geometry': 'LineString',
 'properties': OrderedDict([('FNODE_', 'int:11'),
                            ('TNODE_', 'int:11'),
                            ('LPOLY_', 'int:11'),
                            ('RPOLY_', 'int:11'),
                            ('LENGTH', 'float:12.3'),
                            ('HYD2_4M_', 'int:11'),
                            ('HYD2_4M_ID', 'int:11'),
                            ('GBCODE', 'int:6'),
                            ('NAME', 'str:60'),
                            ('LEVEL_RIVE', 'int:1'),
                            ('LEVEL_LAKE', 'int:1')])}
{'geometry': 'Point',
 'properties': OrderedDict([('PK_UID', 'int:4'),
                            ('Name', 'str:62'),
                            ('Peoples', 'int:7'),
                            ('LocalCounc', 'int:1'),
                            ('County', 'int:1'),
                            ('Region', 'int:1')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('PAC', 'int:10'),
                            ('NAME', 'str:60'),
                            ('POPU', 'float:13.11'),
                            ('AREA', 'float:13.11'),
                            ('Shape_Leng', 'float:19.11'),
                            ('Shape_Area', 'float:19.11')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('id', 'str:80'),
                            ('level', 'int:10'),
                            ('source', 'str:80'),
                            ('parent_id', 'int:10'),
                            ('sibling_id', 'int:10'),
                            ('area', 'float:19.11'),
                            ('Shape_Leng', 'float:19.11'),
                            ('Shape_Area', 'float:19.11')])}
{'geometry': 'Point',
 'properties': OrderedDict([('name', 'str:100'),
                            ('lat', 'float:13.11'),
                            ('lon', 'float:13.11')])}
{'geometry': 'Point',
 'properties': OrderedDict([('CAT', 'int:16'),
                            ('FIPS_CNTRY', 'str:80'),
                            ('CNTRY_NAME', 'str:80'),
                            ('AREA', 'float:17.2'),
                            ('POP_CNTRY', 'float:17.2'),
                            ('ORIG_FID', 'int:10')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('code4', 'int:5'),
                            ('name', 'str:50'),
                            ('code2', 'int:5'),
                            ('pname', 'str:50'),
                            ('popu', 'float:13.11')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('id', 'str:80'),
                            ('level', 'int:10'),
                            ('source', 'str:80'),
                            ('parent_id', 'int:10'),
                            ('sibling_id', 'int:10'),
                            ('area', 'float:19.11'),
                            ('Shape_Leng', 'float:19.11'),
                            ('Shape_Area', 'float:19.11')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('code4', 'int:5'),
                            ('name', 'str:50'),
                            ('code2', 'int:5'),
                            ('pname', 'str:50'),
                            ('popu', 'float:13.11')])}
{'geometry': 'Polygon', 'properties': OrderedDict([('FID', 'int:11')])}
{'geometry': 'Polygon',
 'properties': OrderedDict([('id', 'str:80'),
                            ('level', 'int:10'),
                            ('source', 'str:80'),
                            ('parent_id', 'int:10'),
                            ('sibling_id', 'int:10'),
                            ('area', 'float:24.15')])}

层还可以由索引来指定。

>>> for i, name in enumerate(fiona.listlayers(datasrc_path)):
>>>     with fiona.open(datasrc_path, layer=i) as c:
>>>         print(len(c))
153113
2143
8101
2908
1765
34
3784
349
2466
349
1
5707

若没有指定层, fiona.open 会返回第一层的开放集 。

>>> with fiona.open(datasrc_path) as c:
>>>     c.name == fiona.listlayers(datasrc_path)[0]

要打开一个只读的shapefile,最简单的方法就是 fiona.open 函数,可以把其文件名作为一个命名图层的数据源。 如:

fiona.open('/tmp/foo.shp', 'r', layer='foo')

在实践中,依靠隐含的第一层和默认 'r' 模式是很实用的。

写入多层数据

要写一个全新的多层源数据,只需提供唯一的名字到 layer 参数中即可。

>>> 'wah' not in fiona.listlayers(datasrc_path)
True
>>> datasrc_path = '/tmp/data'
>>> with fiona.open(datasrc_path, layer='bar') as c:
>>>     with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
>>>         d.write(next(iter(c)))
>>> fiona.listlayers(datasrc_path)
['bar', 'wah', 'foo']

'w' 模式中,如果指定的话,会覆盖原有的层,就像是Python的open功能似的。

>>> 'wah' in fiona.listlayers(datasrc_path)
True
>>> with fiona.open(datasrc_path, layer='bar') as c:
>>>     with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
>>>         pass