>>> from env_helper import info; info()
页面更新时间: 2022-12-28 08:13:17
运行环境:
Linux发行版本: Debian GNU/Linux 11 (bullseye)
操作系统内核: Linux-5.10.0-20-amd64-x86_64-with-glibc2.31
Python版本: 3.9.2
4.6. 使用Fiona¶
4.6.1. 读取矢量数据¶
读取GIS矢量文件时,可用 Fiona的 open
函数,再用 'r'
参数打开。返回类型为 fiona.collection.Collection
。
>>> import fiona
>>> c = fiona.open('/gdata/prov_capital.shp', 'r')
>>> c.closed
False
其中 'r'
为缺省参数。
Fiona的 Collection
类似于Python的
file
,但它返回的是迭代器,而不是文本行。
>>> next(c)
<ipython-input-4-e846efec376d>:1: FionaDeprecationWarning: Collection.__next__() is buggy and will be removed in Fiona 2.0. Switch to next(iter(collection)). next(c)
{'type': 'Feature',
'id': '0',
'properties': OrderedDict([('name', '乌鲁木齐'),
('lat', 43.7818),
('lon', 87.5761)]),
'geometry': {'type': 'Point',
'coordinates': (87.57610577000008, 43.78176563200003)}}
>>> next(iter(c))
{'type': 'Feature',
'id': '0',
'properties': OrderedDict([('name', '乌鲁木齐'),
('lat', 43.7818),
('lon', 87.5761)]),
'geometry': {'type': 'Point',
'coordinates': (87.57610577000008, 43.78176563200003)}}
>>> len(list(c))
34
注:list
接口覆盖的是整个 collection
,就像操作
Python文件对象一样,可有效地清除它。对于遍历过的 collection
对象,不支持查找前面的文件,你必须重新打开集合,才可以返回初始部分。
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> len(list(c))
34
注解:文件编码
格式驱动会尝试去检测数据的编码,但有可能会失败。例如GDAL
1.7.2就检测不到地球自然数据编码Windows-1252。在这种情况下,就会指定用:py:func:fiona.open
:
encoding='Windows-1252'
函数的encoding
关键字参数来指定相应编码。版本为0.9.1。
索引集¶
版本为1.1.6,也可通过指数进入特性集。
>>> from pprint import pprint
>>> with fiona.open('/gdata/prov_capital.shp') as src:
>>> pprint(src[1])
{'geometry': {'coordinates': (91.16312837700008, 29.710352750000027),
'type': 'Point'},
'id': '1',
'properties': OrderedDict([('name', '拉萨'),
('lat', 29.7104),
('lon', 91.1631)]),
'type': 'Feature'}
关闭文件¶
:py:class:~fiona.collection.Collection
包含外部资源。除非用
:py:keyword:with
statement明确
:py:meth:~fiona.collection.Collection.close
对象,否则不能保证资源会被释放。当:py:class:~fiona.collection.Collection
处于上下文管理器时,无论发生什么(是否有异常发生),都会被关闭。
>>> try:
>>> with fiona.open('/gdata/prov_capital.shp') as c:
>>> print(len(list(c)))
>>> # assert True is False
>>> except:
>>> print(c.closed)
>>> raise
34
有一项特殊,当:py:keyword:with
模块raise时,你可以查看到输出表:keyword:except
、:py:meth:c.__exit__
(从而导致:py:meth:c.close
) 。
重点强调:
经常称作为:py:meth:~fiona.collection.Collection.close
或使用
:keyword:with
,而且你不会碰到资源外包与锁定文件等情况。
4.6.2. 格式的驱动,CRS,界限和图式¶
4.6.3. 数据的驱动¶
除了类似:py:class:file
(:py:attr:~file.name
,
:py:attr:~file.mode
, :py:attr:~file.closed
)属性外,
:py:class:~fiona.collection.Collection
有一个只读的:py:attr:~fiona.collection.Collection.driver
属性,
为:program:OGR
:dfn:format driver
的属性,用于打开矢量文件。
>>> import fiona
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> c.driver
'ESRI Shapefile'
4.6.4. 数据的投影参数及转换方法¶
矢量数据集的coordinate reference system
(CRS) 可通过只读的
:py:attr:~fiona.collection.Collection.crs
属性来访问。由于数据集的参数原因,可能会出现不同的结果。
>>> c.crs
{'init': 'epsg:4326'}
{‘no_defs’: True, ‘ellps’: ‘WGS84’, ‘datum’: ‘WGS84’, ‘proj’: ‘longlat’}
>>> c.crs
{'init': 'epsg:4326'}
CRS返回的结果,是对 :program:PROJ.4
参数的映射。
:py:mod:fiona.crs
模块共有3个函数,以协助完成这些映射。
:py:func:~fiona.crs.to_string
函数是将映射转换到PROJ.4字符串中:
>>> from fiona.crs import to_string
>>> print(to_string(c.crs))
+init=epsg:4326
:py:func:~fiona.crs.from_string
为不可逆。
>>> from fiona.crs import from_string
>>> from_string("+datum=WGS84 +ellps=WGS84 +no_defs +proj=longlat")
{'datum': 'WGS84', 'ellps': 'WGS84', 'no_defs': True, 'proj': 'longlat'}
:py:func:~fiona.crs.from_epsg
是EPSG代码CRS映射的一个捷径。
>>> from fiona.crs import from_epsg
>>> from_epsg(3857)
{'init': 'epsg:3857', 'no_defs': True}
4.6.5. 数据集中要素的数目、范围¶
可通过Python的 :py:func:len
函数获取所有的收集记录。
>>> len(c)
34
收集记录的minimum bounding rectangle
(MBR) 或 bounds
可通过只读的~fiona.collection.Collection.bounds
属性来获取。
>>> c.bounds
(87.57610577000008, 19.97013978600006, 126.56611955000005, 45.69344403700006)
4.6.6. 数据图表(Schema)¶
最后,记录类型的Schema(矢量文件是单一类型记录)可通过只读的
:py:attr:~fiona.collection.Collection.schema
属性访问。它有“几何”和“属性”参数
。前者是一个字符串,后者是一个有序的库,具有相同命令的参数。
>>> from pprint import pprint
>>> import fiona
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> pprint(c.schema)
{'geometry': 'Point',
'properties': OrderedDict([('name', 'str:100'),
('lat', 'float:13.11'),
('lon', 'float:13.11')])}
保持架构简单¶
Fiona可减少更多的记录类型和模式。记录由数据组成。模型记录的是’id’关键词,模型映射与映射集的关键词一致。
>>> rec = next(c)
>>> set(rec.keys()) - set(c.schema.keys())
<ipython-input-19-b2475a4fc54b>:1: FionaDeprecationWarning: Collection.__next__() is buggy and will be removed in Fiona 2.0. Switch to next(iter(collection)). rec = next(c)
{'id', 'type'}
>>> set(rec['properties'].keys()) == set(c.schema['properties'].keys())
True
模式映射值也可以是字段类型,如 ‘Polygon’, ‘float’,
‘str’。相应的Python类型可以在fiona.FIELD_TYPES_MAP
库中查到。
>>> pprint(fiona.FIELD_TYPES_MAP)
{'bytes': <class 'bytes'>,
'date': <class 'fiona.rfc3339.FionaDateType'>,
'datetime': <class 'fiona.rfc3339.FionaDateTimeType'>,
'float': <class 'float'>,
'int': <class 'int'>,
'int32': <class 'int'>,
'int64': <class 'int'>,
'str': <class 'str'>,
'time': <class 'fiona.rfc3339.FionaTimeType'>}
字段类型¶
简而言之,他们的命名、类型与Python(或JavaScript)相近。‘str’与 ’unicode’混合功能只在Python3.0版本以下才会有。 Fiona记录的是Unicode字符串,其字段类型均命令为 ’str’。
>>> rec['properties']
OrderedDict([('name', '乌鲁木齐'), ('lat', 43.7818), ('lon', 87.5761)])
>>> type(rec['properties']['name'])
str
>>> c.schema['properties']['name']
'str:100'
字符串字段可限制最大宽度。 str:25
设置的就是不可以超过25个字符。
如果这个值用于打开文件,该值的属性就是将在25字符处截断。
默认宽度为80个字符,这意味着 str
和 str:80
属同一意思。
Fiona还有一个函数:可获取字符串属性宽度。
>>> from fiona import prop_width
>>> prop_width('str:25')
25
>>> prop_width('str')
80
另一个函数是可获取Python属性类型。
>>> from fiona import prop_type
>>> prop_type('int')
int
>>> prop_type('float')
float
>>> prop_type('str:25')
str
以上的例子针对于Python 3。Python 2'str'
的性能是
'unicode'
。
几何类型¶
Fiona 支持 GeoJSON 和三维变异的几何类型,几何元素的架构值如下:
点
线
多边形
点集合
线集合
面集合
混合数据类型
三维点
三维线
三维面
三维点集合
三维线集合
三维面集合
三维混合数据类型
后七个3D类型,只适用于集合模式。几何特征类型基本对应的是七个中的第一个。例如, ’三维点 ’集 ,总是与几何式的点特性对应。这些几何坐标就是(x,y,Z)元组。
注意,一个最常见的矢量数据格式,ESRI Shapefile,是没有’线集合’或’面集合’的图式结构。因此,一个shapefile ‘面’ 可以是’面’,也可以是’面集合’。
4.6.7. 记录¶
Python的 dict
结构化与GeoJSON特征相似。Fiona可以自描述,其字段的命名包含在数据结构和字段中。数值字段的值类型就是
int
和 float
,不是字符串格式。
>>> import fiona
>>> from pprint import pprint
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> rec = c.next()
>>> pprint(rec)
{'geometry': {'coordinates': (87.57610577000008, 43.78176563200003),
'type': 'Point'},
'id': '0',
'properties': OrderedDict([('name', '乌鲁木齐'),
('lat', 43.7818),
('lon', 87.5761)]),
'type': 'Feature'}
<ipython-input-30-a87b01bdde07>:4: FionaDeprecationWarning: Collection.__next__() is buggy and will be removed in Fiona 2.0. Switch to next(iter(collection)). rec = c.next()
此条数据记录与本源或其他外部资源的 :py:class:~fiona.collection
无关。它是完全独立的,使用任何方式都很安全。关闭集并不影响数据记录。
>>> c.close()
>>> rec['id']
'0'
记录ID¶
每一条记录都有
id
号。根据GeoJSON规范,在数据文件中每一字符串都有相应且唯一的
id
值。
>>> c = fiona.open('/gdata/prov_capital.shp')
>>> rec = next(c)
>>> rec['id']
<ipython-input-32-e69da5821247>:2: FionaDeprecationWarning: Collection.__next__() is buggy and will be removed in Fiona 2.0. Switch to next(iter(collection)). rec = next(c)
'0'
在 :program:OGR
模型中,ID号是长整数。因此在记录整数索引时,通常以字符串为表示形式。
记录属性¶
每条记录都有其
属性
,其对应值就是一个映射,任一有序的库其映射值都特别精确。映射属性与同源属性集的模式相同(见上文)。
>>> pprint(rec['properties'])
OrderedDict([('name', '乌鲁木齐'), ('lat', 43.7818), ('lon', 87.5761)])
几何记录¶
每条记录都有几何属性,其对应值是类型与坐标映射。
>>> pprint(rec['geometry'])
{'coordinates': (87.57610577000008, 43.78176563200003), 'type': 'Point'}
类型 | 坐标 |
点 | 单一(x,y)元组 |
线 | (x,y)元组顶点列表 |
多边形 | 环列表[每个(x,y)元组列表] |
点集合 | 点列表[每个(x,y)元组列表] |
线集合 | 线列表[每个(x,y)元组列表] |
面集合 | 多边形列表 |
Fiona,类似于GeoJSON格式,既有北半球的“北方”,又有笛卡尔的“X-Y”偏角。上文说的元组值
(x, y)
,要么是(本初子午线的经度E、纬度N),要么是其他投影坐标系统(东,北)。
真正顺序是经-纬,而不是纬-经,尽管我们大多都在说 “纬度,经度” ,Fiona
x,y
基本都是东向和北向,也就是
(经, 纬)
。先说经度,后说纬度,与GeoJSON规范保持一致。
点集理论和简易特性¶
在一个适当且干净的矢量数据文件中,几何映射是指几何对象由 point sets
组成,如下所示。
>>> from shapely.geometry import shape
>>> l1 = shape({'type': 'LineString', 'coordinates': [(0, 0), (2, 2)]})
>>> l2 = shape({'type': 'LineString', 'coordinates': [(0, 0), (1, 1), (2, 2)]})
>>> l1 == l2
False
>>> l1.equals(l2)
True
注解:Dirty数据,某些文件可能会包含矢量数据 :dfn:invalid
(在生成结果的质量控制不足时)或intention(“dirty”矢量保存到一个特殊的文件)。Fiona不会清除dirty数据,所以你应该确保你所得到的是否为纯数据。
4.6.8. 矢量数据写法¶
一个矢量文件可以通过模式 'a'
(append) 打开,也可以通过模式 'w'
(write)写入。
admonition:: Note
OGR
的 “update” 模式没有独立格式,因此,Fiona不支持。
添加数据到现有文件¶
>>> import fiona
>>> with fiona.open('/gdata/prov_capital.shp') as c:
>>> rec = next(c)
>>> rec['id'] = '-1'
>>> rec['properties']['CNTRY_NAME'] = 'Gondor'
<ipython-input-37-8f885b1fa055>:3: FionaDeprecationWarning: Collection.__next__() is buggy and will be removed in Fiona 2.0. Switch to next(iter(collection)). rec = next(c)
>>> from fiona import os
>>> os.system("cp /gdata/prov_capital.* /tmp")
0
注意,在文件修改前要拷贝备份。
这样,坐标参考系统、.format
与文件架构就定义完成,所以他不能以只读方式打开,也不能是
'a'
模式。新的记录用:py:meth:~fiona.collection.Collection.write
的方式写在文件最尾处,因此,该文件的长度就从48增加到了49。
>>> import os, stat
>>> shp_file = '/tmp/prov_capital'
>>> os.chmod(shp_file + '.shp', stat.S_IRUSR + stat.S_IWUSR)
>>> os.chmod(shp_file + '.dbf', stat.S_IRUSR + stat.S_IWUSR)
>>> os.chmod(shp_file + '.shx', stat.S_IRUSR + stat.S_IWUSR)
>>> with fiona.open(shp_file + '.shp', 'a') as c:
>>> print(len(c))
34
你写入的记录必须匹配文件模式(因为一个文件包含一个记录型)。如果不是的话,会出现
ValueError
的异常。
with fiona.open(shp_file + '.shp', 'a') as c:
c.write({'properties': {'foo': 'bar'}})
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
若忽略一个记录的ID值,可能就会被下一个生成的值替换掉。如果你只是读文件,仅参考上面的即可,
>>> shp_file = '/tmp/prov_capital'
>>> os.chmod(shp_file + '.shp', stat.S_IRUSR + stat.S_IWUSR)
>>> os.chmod(shp_file + '.dbf', stat.S_IRUSR + stat.S_IWUSR)
>>> os.chmod(shp_file + '.shx', stat.S_IRUSR + stat.S_IWUSR)
>>> with fiona.open('/tmp/prov_capital.shp') as c:
>>> records = list(c) # records = c.next()
>>> records[-1]['id']
'33'
>>> records[-1]['properties']['name']
'澳门'
你可能还会看到ID值为 '-1'
,这记录有时可能会被 '48'
替换掉。
:py:meth:~fiona.collection.Collection.
是文件集的唯一记录。:py:meth:~fiona.collection.Collection.writerecords
的写入会成为序列值(或迭代器)。
>>> with fiona.open('/tmp/prov_capital.shp', 'a') as c:
>>> c.writerecords([rec, rec,rec])
>>> print(len(c))
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-43-26937f503ada> in <module>
1 with fiona.open('/tmp/prov_capital.shp', 'a') as c:
----> 2 c.writerecords([rec, rec,rec])
3 print(len(c))
/usr/lib/python3/dist-packages/fiona/collection.py in writerecords(self, records)
359 if self.mode not in ('a', 'w'):
360 raise IOError("collection not open for writing")
--> 361 self.session.writerecs(records, self)
362 self._len = self.session.get_length()
363 self._bounds = None
fiona/ogrext.pyx in fiona.ogrext.WritingSession.writerecs()
ValueError: Record does not match collection schema: odict_keys(['name', 'lat', 'lon', 'CNTRY_NAME']) != ['name', 'lat', 'lon']
复制。Fiona集有复制功能。以上的代码共复制了3次,而且它们都有独立的ID号。
缓冲。Fiona输出有缓冲功能。在集关闭后,
write
和writerecords
在磁盘内会刷新。你也可以叫作flush
,会定期写进磁盘的缓冲区域。
写入新文件¶
写一个新文件比添加现有文件要复杂,因为
CRS、格式、模式等都尚未创建,而且这些都是必做的。解决方法就是上面所述的映射模式。CRS有映射功能,
fiona.drivers
列表基本把可能的CRS映射格式都已列举出来。
>>> with fiona.open('/gdata/prov_capital.shp') as source:
>>> source_driver = source.driver
>>> source_crs = source.crs
>>> source_schema = source.schema
>>> print(source_driver)
ESRI Shapefile
>>>
>>> source_crs
{'init': 'epsg:4326'}
>>> from pprint import pprint
>>> pprint(source_schema)
{'geometry': 'Point',
'properties': OrderedDict([('name', 'str:100'),
('lat', 'float:13.11'),
('lon', 'float:13.11')])}
现在创建一个新文件。
>>> with fiona.open( '/tmp/foo.shp', 'w', driver=source_driver, crs=source_crs, schema=source_schema) as c:
>>> print(len(c))
0
>>> print(len(c))
0
>>> c.closed
True
>>> len(c)
0
由于对源架构的性能有要求,可以在写入的模式集中使用同一命令,书面文件的字段可与源文件使用同一命令。
ogrinfo '/tmp/foo.shp' foo -so
INFO: Open of `/tmp/foo.shp'
using driver `ESRI Shapefile' successful.
~fiona.collection.Collection.meta
属性可以使复制文件元属性更加容易。
>>> source = fiona.open('/gdata/prov_capital.shp')
>>> sink = fiona.open('/tmp/foo.shp', 'w', **source.meta)
Ordering Record Fields排序字段¶
在Fiona 1.0.1中, fiona.open
的 ’schema
’参数就是一个有序的库列表(键,值),并指定一个序列。如果给定一个常规库,用输出
~items
库的方法来排序。例如,
>>> {'bar': 'int', 'foo': 'str'}.keys()
dict_keys(['bar', 'foo'])
{'properties': {'bar': 'int', 'foo': 'str'}}
模式可生成
shapefile,第一个字段是 ‘foo’ ,第二个字段是 ‘bar’ 。如果你想要把 ‘bar’
作为第一个字段,你必须用一个属性列表项。
另外,要注意在 schema
要声明 geometry
,其类型为 Polygon
,注意大小写。
>>> c = fiona.open( '/tmp/foo2.shp', 'w',schema={'properties': [('bar', 'int'), ('foo', 'str')], 'geometry': 'Polygon'}, driver = 'ESRI Shapefile')
或一个有序的库。
>>> from collections import OrderedDict
>>> schema_props = OrderedDict([('bar', 'int'), ('foo', 'str')])
>>> c = fiona.open('/tmp/foo.shp','w',schema={'properties': schema_props, 'geometry': 'Polygon'}, driver = 'ESRI Shapefile')
坐标和几何类型¶
如果你写一个三维坐标(X,Y,Z)元组到2D文件(‘point’ 几何模式),Z值就不会显示。
如果你写一个二维坐标(x,y)元组到3D文件(‘3D Point’ 几何模式),默认的Z值就是0。
4.6.9. 多层数据的处理¶
读取多层数据¶
一般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/world_borders.shp world_borders -nln foo'
>>> cmd2 = 'ogr2ogr /tmp/data/ /gdata/world_borders.shp world_borders -nln bar'
>>> subprocess.call(cmd1, shell=True)
>>> subprocess.call(cmd2, shell=True)
1
一个数据源层可以用函数fiona.listlayers
列一下,写法是
listlayers()
。在
Shapefile格式的情况下,层的名称需匹配文件库的名称。
>>> import fiona
>>> fiona.listlayers('/gdata')
['spatial_filter',
'world_pt',
'GSHHS_h',
'GSHHS_l_L1',
'world_spatial_filter',
'region_popu2',
'county_popu',
'shape_towns',
'GSHHS_c',
'prov_capital',
'region_popu',
'hyd2_4l']
不像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([('FID', 'int: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([('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([('id', 'str:80'),
('level', 'int:10'),
('source', 'str:80'),
('parent_id', 'int:10'),
('sibling_id', 'int:10'),
('area', 'float:24.15')])}
{'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([('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': '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([('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': 'Polygon',
'properties': OrderedDict([('code4', 'int:5'),
('name', 'str:50'),
('code2', 'int:5'),
('pname', 'str:50'),
('popu', 'float:13.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')])}
层还可以由索引来指定。
>>> for i, name in enumerate(fiona.listlayers(datasrc_path)):
>>> with fiona.open(datasrc_path, layer=i) as c:
>>> print(len(c))
1
3784
153113
5707
2466
349
2908
8101
1765
34
349
2143
若没有指定层, 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')
<open Collection '/tmp/foo.shp:foo', mode 'r' at 0x7f414cfc2c40>
在实践中,依靠隐含的第一层和默认 'r'
模式是很实用的,打开shapefile:
>>> fiona.open('/tmp/foo.shp')
<open Collection '/tmp/foo.shp:foo', mode 'r' at 0x7f414cfc27c0>
写入多层数据¶
要写一个全新的多层源数据,只需提供唯一的名字到 layer
参数中即可。
>>> 'wah' not 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:
>>> d.write(next(c))
>>>
>>>
>>> fiona.listlayers(datasrc_path)
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-64-c9c3668dbf5b> in <module>
----> 1 with fiona.open(datasrc_path, layer='bar') as c:
2 with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
3 d.write(next(c))
4
5
/usr/lib/python3/dist-packages/fiona/env.py in wrapper(*args, **kwargs)
407 with Env.from_defaults(session=session):
408 log.debug("Credentialized: {!r}".format(getenv()))
--> 409 return f(*args, **kwargs)
410 return wrapper
411
/usr/lib/python3/dist-packages/fiona/__init__.py in open(fp, mode, driver, schema, crs, encoding, layer, vfs, enabled_drivers, crs_wkt, **kwargs)
254
255 if mode in ('a', 'r'):
--> 256 c = Collection(path, mode, driver=driver, encoding=encoding,
257 layer=layer, enabled_drivers=enabled_drivers, **kwargs)
258 elif mode == 'w':
/usr/lib/python3/dist-packages/fiona/collection.py in __init__(self, path, mode, driver, schema, crs, encoding, layer, vsi, archive, enabled_drivers, crs_wkt, ignore_fields, ignore_geometry, **kwargs)
160 if self.mode == 'r':
161 self.session = Session()
--> 162 self.session.start(self, **kwargs)
163 elif self.mode in ('a', 'w'):
164 self.session = WritingSession()
fiona/ogrext.pyx in fiona.ogrext.Session.start()
ValueError: Null layer: 'bar'
在 'w'
模式中,如果指定的话,会覆盖原有的层,就像是Python的open
功能似的。
>>> 'wah' in fiona.listlayers(datasrc_path)
False
>>> with fiona.open(datasrc_path, layer='bar') as c:
>>> with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
>>> pass
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
<ipython-input-66-d932f7209f35> in <module>
----> 1 with fiona.open(datasrc_path, layer='bar') as c:
2 with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
3 pass
/usr/lib/python3/dist-packages/fiona/env.py in wrapper(*args, **kwargs)
407 with Env.from_defaults(session=session):
408 log.debug("Credentialized: {!r}".format(getenv()))
--> 409 return f(*args, **kwargs)
410 return wrapper
411
/usr/lib/python3/dist-packages/fiona/__init__.py in open(fp, mode, driver, schema, crs, encoding, layer, vfs, enabled_drivers, crs_wkt, **kwargs)
254
255 if mode in ('a', 'r'):
--> 256 c = Collection(path, mode, driver=driver, encoding=encoding,
257 layer=layer, enabled_drivers=enabled_drivers, **kwargs)
258 elif mode == 'w':
/usr/lib/python3/dist-packages/fiona/collection.py in __init__(self, path, mode, driver, schema, crs, encoding, layer, vsi, archive, enabled_drivers, crs_wkt, ignore_fields, ignore_geometry, **kwargs)
160 if self.mode == 'r':
161 self.session = Session()
--> 162 self.session.start(self, **kwargs)
163 elif self.mode in ('a', 'w'):
164 self.session = WritingSession()
fiona/ogrext.pyx in fiona.ogrext.Session.start()
ValueError: Null layer: 'bar'
4.6.10. 高级主题¶
切片和masking迭代器¶
若想添加空间矢量数据格式,可查找bounding
box。函数:py:meth:~fiona.collection.Collection.items
返回一个FIDs迭代器,并记录交叉指定的 (minx, miny, maxx, maxy)
bounding
box或几何对象。自己的坐标参考系统可用来解译box值。如果你要查看迭代器列表,可通过Python的builtin
:py:func: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)
迭代器与切割参数itertools.islice
的 stop
或
start, stop[, step]
的功能相同。要想查找迭代器的前两个参数,暂停即可。
>>> hits = c.items(2, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
要想得到迭代器第三到第五个参数,启动和停止即可。
>>> hits = c.items(2, 5, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
要想过滤属性值,可使用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))