import fiona
c = fiona.open('/gdata/prov_capital.shp', 'r')
c.closed
其中 'r'
为缺省参数。
Fiona的 Collection
类似于Python的 file
,但它返回的是迭代器,而不是文本行。
next(c)
len(list(c))
注:list
接口覆盖的是整个 collection
,就像操作 Python文件对象一样,可有效地清除它。对于遍历过的 collection
对象,不支持查找前面的文件,你必须重新打开集合,才可以返回初始部分。
c = fiona.open('/gdata/prov_capital.shp')
len(list(c))
from pprint import pprint
with fiona.open('/gdata/prov_capital.shp') as src:
pprint(src[1])
try:
with fiona.open('/gdata/prov_capital.shp') as c:
print(len(list(c)))
# assert True is False
except:
print(c.closed)
raise
有一项特殊,当:py:keyword:with
模块raise时,你可以查看到输出表:keyword:except
、:py:meth:c.__exit__
(从而导致:py:meth:c.close
) 。
重点强调: 经常称作为:py:meth:~fiona.collection.Collection.close
或使用 :keyword:with
,而且你不会碰到资源外包与锁定文件等情况。
import fiona
c = fiona.open('/gdata/prov_capital.shp')
c.driver
c.crs
{'no_defs': True, 'ellps': 'WGS84', 'datum': 'WGS84', 'proj': 'longlat'}
c.crs
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))
:py:func:~fiona.crs.from_string
为不可逆。
from fiona.crs import from_string
from_string("+datum=WGS84 +ellps=WGS84 +no_defs +proj=longlat")
:py:func:~fiona.crs.from_epsg
是EPSG代码CRS映射的一个捷径。
from fiona.crs import from_epsg
from_epsg(3857)
len(c)
收集记录的minimum bounding rectangle
(MBR) 或 bounds
可通过只读的~fiona.collection.Collection.bounds
属性来获取。
c.bounds
from pprint import pprint
import fiona
c = fiona.open('/gdata/prov_capital.shp')
pprint(c.schema)
rec = next(c)
set(rec.keys()) - set(c.schema.keys())
set(rec['properties'].keys()) == set(c.schema['properties'].keys())
模式映射值也可以是字段类型,如 'Polygon', 'float', 'str'。相应的Python类型可以在fiona.FIELD_TYPES_MAP
库中查到。
pprint(fiona.FIELD_TYPES_MAP)
type(rec['properties']['CNTRY_NAME'])
c.schema['properties']['CNTRY_NAME']
字符串字段可限制最大宽度。'str:25'设置的就是不可以超过25个字符。如果这个值用于打开文件,该值的属性就是将在25字符处截断。默认宽度为80个字符,这意味着 'str '和 'str:80' 属同一意思。
Fiona还有一个函数:可获取字符串属性宽度。
from fiona import prop_width
prop_width('str:25')
prop_width('str')
另一个函数是可获取Python属性类型。
from fiona import prop_type
prop_type('int')
prop_type('float')
prop_type('str:25')
以上的例子针对于Python 3。Python 2'str'
的性能是 'unicode'
。
import fiona
from pprint import pprint
c = fiona.open('/gdata/prov_capital.shp')
rec = c.next()
pprint(rec)
此条数据记录与本源或其他外部资源的 :py:class:~fiona.collection
无关。它是完全独立的,使用任何方式都很安全。关闭集并不影响数据记录。
c.close()
rec['id']
c = fiona.open('/gdata/prov_capital.shp')
rec = next(c)
rec['id']
pprint(rec['properties'])
pprint(rec['geometry'])
类型 | 坐标 |
点 | 单一(x,y)元组 |
线 | (x,y)元组顶点列表 |
多边形 | 环列表[每个(x,y)元组列表] |
点集合 | 点列表[每个(x,y)元组列表] |
线集合 | 线列表[每个(x,y)元组列表] |
面集合 | 多边形列表 |
Fiona,类似于GeoJSON格式,既有北半球的“北方”,又有笛卡尔的“X-Y”偏角。上文说的元组值 (x, y)
,要么是(本初子午线的经度E、纬度N),要么是其他投影坐标系统(东,北)。
真正顺序是经-纬,而不是纬-经,尽管我们大多都在说 "纬度,经度" ,Fiona x,y
基本都是东向和北向,也就是 (经, 纬)
。先说经度,后说纬度,与GeoJSON规范保持一致。
点集理论和简易特性
在一个适当且干净的矢量数据文件中,几何映射是指几何对象由 :dfn:point sets
组成,如下所示。
sourcecode:: python
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
l1.equals(l2)
注解:Dirty数据,某些文件可能会包含矢量数据 :dfn:invalid
(在生成结果的质量控制不足时)或intention(“dirty”矢量保存到一个特殊的文件)。Fiona不会清除dirty数据,所以你应该确保你所得到的是否为纯数据。
import fiona
with fiona.open('/gdata/prov_capital.shp') as c:
rec = next(c)
rec['id'] = '-1'
rec['properties']['CNTRY_NAME'] = 'Gondor'
from fiona import os
os.system("cp /gdata/prov_capital.* /tmp")
注意,在文件修改前要拷贝备份。
这样,坐标参考系统、.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))
你写入的记录必须匹配文件模式(因为一个文件包含一个记录型)。如果不是的话,会出现 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']
records[-1]['properties']['CNTRY_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))
with fiona.open('/gdata/prov_capital.shp') as source:
source_driver = source.driver
source_crs = source.crs
source_schema = source.schema
print(source_driver)
source_crs
from pprint import pprint
pprint(source_schema)
现在创建一个新文件。
with fiona.open( '/tmp/foo.shp', 'w', driver=source_driver, crs=source_crs, schema=source_schema) as c:
print(len(c))
print(len(c))
c.closed
len(c)
由于对源架构的性能有要求,可以在写入的模式集中使用同一命令,书面文件的字段可与源文件使用同一命令。
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)
{'bar': 'int', 'foo': 'str'}.keys()
{'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')
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)
一个数据源层可以用函数fiona.listlayers
列一下,写法是 listlayers()
。在 Shapefile格式的情况下,层的名称需匹配文件库的名称。
import fiona
fiona.listlayers('/tmp/data')
不像OGR,Fiona没有层与数据源的分类。要想访问层,需用数据源路径打开一个集,并用 layer
来指定该层。
import pprint
datasrc_path = '/tmp/data'
for name in fiona.listlayers(datasrc_path):
with fiona.open(datasrc_path, layer=name) as c:
pprint.pprint(c.schema)
层还可以由索引来指定。
for i, name in enumerate(fiona.listlayers(datasrc_path)):
with fiona.open(datasrc_path, layer=i) as c:
print(len(c))
若没有指定层, fiona.open
会返回第一层的开放集 。
with fiona.open(datasrc_path) as c:
c.name == fiona.listlayers(datasrc_path)[0]
要打开一个可读的shapefile,最简单的方法就是fiona.open
参数,可以把它作为一个命名图层的数据源。
fiona.open('/tmp/data/foo.shp', 'r', layer='foo')
在实践中,依靠隐含的第一层和默认 'r'
模式是很实用的,打开shapefile:
fiona.open('/tmp/data/foo.shp')
'wah' not in fiona.listlayers(datasrc_path)
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)
在 'w'
模式中,如果指定的话,会覆盖原有的层,就像是Python的open
功能似的。
'wah' in fiona.listlayers(datasrc_path)
with fiona.open(datasrc_path, layer='bar') as c:
with fiona.open(datasrc_path, 'w', layer='wah', **c.meta) as d:
pass
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))