1 Fiona用户手册¶
- 作者
Sean Gillies, <sean.gillies@gmail.com>
- 版本
2.0dev
- 日期
2020 年 12 月 16 日
- 版权
除代码示例外,此工作是根据 `Creative Commons Attribution 3.0 United States License`_ _. 这些代码示例在BSD 3条款许可下获得许可(请参见许可证.txt在存储库根目录中)。
- 摘要
Fiona是OGR的简洁,灵活,严谨的API。本文档介绍如何使用Fiona软件包读取和写入地理空间数据文件。在示例中使用了python 3。参见 README 用于安装和快速启动说明。
1.1 介绍¶
Geographic information systems (GIS)帮助我们规划、应对和理解我们的物理、政治、经济和文化景观的变化。一代人之前,GIS只是由国家和城市等主要机构完成的,但由于精确和廉价的全球定位系统、卫星图像的商品化以及开源软件,它如今变得无处不在。
GIS的数据类型大致分为 rasters 表示连续的标量场(例如地表温度或海拔)和 vectors 代表离散的实体,如道路和行政边界。Fiona只关心后者。它是一个python包装器,用于 OGR 类库。一个非常简单的包装极简主义者。它以类似geojson的映射的形式从文件中读取数据记录,并将与记录相同的映射写回文件。就是这样。没有层、没有光标、没有几何操作、没有坐标系之间的转换、没有远程方法调用;所有这些问题都留给其他的Python包,如 Shapely
和 pyproj
以及Python语言协议。为什么?消除不必要的并发症。菲奥娜的目标是简单易懂,易于使用,没有任何问题。
请理解这一点:FIONA的设计是为了在某些任务范围内表现出色,而在其他任务中则不太理想。Fiona利用内存和速度来实现简单性和可靠性。其中,ogr的python绑定(例如)使用C指针,fiona将矢量数据从数据源复制到python对象。它们使用起来更简单、更安全,但内存更密集。如果您只需要访问一个记录字段,那么Fiona的性能相对较慢——当然,如果您只想重新投影或筛选数据文件,没有什么比 ogr2ogr 程序——但是如果您需要的话,fiona的性能比ogr的python绑定要好得多。 all 记录的字段和坐标。复制是一个约束,但它简化了程序。使用fiona,您不必跟踪对c对象的引用以避免崩溃,并且可以使用熟悉的python映射访问器来处理向量数据。更少的担心,更少的时间花在阅读API文档上。
1.1.1 经验法则¶
在哪些情况下,您会从使用 Fiona 中受益?
如果感兴趣的功能来自或预定用于非文本格式的文件,如ESRI shapefiles、mapinfo tab文件等。
如果您对许多特性属性的值比对单个特性的值更感兴趣。
如果您对特征几何图形的所有坐标值比对单个值更感兴趣。
如果您的处理系统是分布式的或不包含在单个进程中。
在哪些情况下,您不会从使用 Fiona 中受益?
如果您的数据是在JSON文档中,或者是为JSON文档而准备的,那么应该使用python的
json
或simplejson
模块。如果您的数据位于像postgis这样的RDBMS中,请使用python-db包或类似orm的包。
SQLAlchemy
或GeoAlchemy
. 也许你在用GeoDjango
已经。如果是,继续。如果您的数据是通过来自couchdb或cartodb等的http提供的,请使用http包(
httplib2
,Requests
等)或提供程序的python api。如果你能用 ogr2ogr 这样做。
1.1.2 例子¶
使用fiona的第一个例子是:将记录从一个文件复制到另一个文件,添加两个属性,并确保所有多边形都朝上。在某些应用中,多边形的方向很重要,例如,在GoogleEarth中挤压多边形。没有其他类库(比如 Shapely
)在这里是需要的,这使它不复杂。有一个 test_uk
fiona存储库中的文件,用于本例和其他示例。
import datetime
import logging
import sys
import fiona
logging.basicConfig(stream=sys.stderr, level=logging.INFO)
def signed_area(coords):
"""Return the signed area enclosed by a ring using the linear time
algorithm at http://www.cgafaq.info/wiki/Polygon_Area. A value >= 0
indicates a counter-clockwise oriented ring.
"""
xs, ys = map(list, zip(*coords))
xs.append(xs[1])
ys.append(ys[1])
return sum(xs[i] * (ys[i + 1] - ys[i - 1]) for i in range(1, len(coords))) / 2.0
with fiona.open("docs/data/test_uk.shp", "r") as source:
# Copy the source schema and add two new properties.
sink_schema = source.schema
sink_schema["properties"]["s_area"] = "float"
sink_schema["properties"]["timestamp"] = "datetime"
# Create a sink for processed features with the same format and
# coordinate reference system as the source.
with fiona.open(
"oriented-ccw.shp",
"w",
crs=source.crs,
driver=source.driver,
schema=sink_schema,
) as sink:
for f in source:
try:
# If any feature's polygon is facing "down" (has rings
# wound clockwise), its rings will be reordered to flip
# it "up".
g = f["geometry"]
assert g["type"] == "Polygon"
rings = g["coordinates"]
sa = sum(signed_area(r) for r in rings)
if sa < 0.0:
rings = [r[::-1] for r in rings]
g["coordinates"] = rings
f["geometry"] = g
# Add the signed area of the polygon and a timestamp
# to the feature properties map.
f["properties"].update(
s_area=sa, timestamp=datetime.datetime.now().isoformat()
)
sink.write(f)
except Exception, e:
logging.exception("Error processing feature %s:", f["id"])
# The sink file is written to disk and closed when its block ends.
1.2 数据模型¶
在地理信息系统中,离散的地理特征通常用 records . 记录的特征及其语义含义众所周知[Kent1978]。在那些对地理数据最重要的数据中:记录只有一种类型,该类型的所有记录都有相同的字段,并且记录的字段涉及到一个地理特征。不同的系统模型以不同的方式记录,但是不同的模型有足够的共同点,程序员已经能够创建有用的抽象数据模型。这个 OGR model is one. Its primary entities are Data Sources, Layers 和 Features . 功能没有字段,而是属性和 Geometry . OGR层包含单一类型的特征(例如“道路”或“井”)。geojson模型更加简单,保留了特性并替换了 Feature Collections 用于OGR数据源和层。因此,在地理信息系统建模中,“特征”一词过载,表示概念模型和数据模型中的实体。
存在各种记录文件格式。这个 ESRI Shapefile [Esri1998]在2005年之前,至少在美国是最重要的,并且至今仍然流行。它是一种二进制格式。形状字段存储在一个.shp文件中,其他字段存储在另一个.dbf文件中。geojson[geojson]格式从2008年开始,提出了一种人类可读的文本格式,其中几何图形和其他属性字段使用 Javascript Object Notation [JSON]在geojson中,数据访问是一致的。要素属性的访问方式与要素集合的属性相同。几何图形的坐标以与集合特征相同的方式访问。
GeoJSON格式是一个很好的PythonAPI模型。JSON对象和Python字典在语义和语法上相似。用基于Python映射的接口替换面向对象的层和特性API提供了对数据的统一访问,并减少了读取文档所花费的时间。Python程序员知道如何使用映射,那么为什么不把特性当作字典呢?使用现有的python习惯用法是fiona的主要设计原则之一。
TL;DR
Fiona订阅了传统的数据记录模型,但通过类似python文件和映射协议提供了类似geojson的数据访问。
1.3 读取矢量数据¶
读取GIS矢量文件首先以模式打开 'r'
使用菲奥娜 open()
功能。它返回一个打开的 Collection
对象。
>>> import fiona
>>> c = fiona.open('docs/data/test_uk.shp', 'r')
>>> c
<open Collection 'docs/data/test_uk.shp:test_uk', mode 'r' at 0x...>
>>> c.closed
False
API更改
fiona.collection()
已弃用,但别名为 fiona.open()
在版本0.9中。
模式 'r'
是默认值,将在以下示例中省略。
Fiona的:py:class:~fiona.collection.Collection 就像一条 Python file
,但对于记录而不是行是不可更改的。
>>> next(c)
{'geometry': {'type': 'Polygon', 'coordinates': ...
>>> len(list(c))
48
注意 list()
循环访问整个集合,像使用Python一样有效地清空它 file
.
>>> next(c)
Traceback (most recent call last):
...
StopIteration
>>> len(list(c))
0
不支持查找文件的开头。您必须重新打开集合才能回到开头。
>>> c = fiona.open('docs/data/test_uk.shp')
>>> len(list(c))
48
文件编码
格式驱动程序将尝试检测数据的编码,但可能会失败。根据我的经验,gdal 1.7.2(例如)没有检测到自然地球数据集的编码是windows-1252。在这种情况下,可以使用 encoding
的关键字参数 fiona.open()
: encoding='Windows-1252'
.
0.9.1版新增的功能。
1.3.1 集合索引¶
集合的特性也可以通过索引访问。
>>> import pprint
>>> with fiona.open('docs/data/test_uk.shp') as src:
... pprint.pprint(src[1])
...
{'geometry': {'coordinates': [[(-4.663611, 51.158333),
(-4.669168, 51.159439),
(-4.673334, 51.161385),
(-4.674445, 51.165276),
(-4.67139, 51.185272),
(-4.669445, 51.193054),
(-4.665556, 51.195),
(-4.65889, 51.195),
(-4.656389, 51.192215),
(-4.646389, 51.164444),
(-4.646945, 51.160828),
(-4.651668, 51.159439),
(-4.663611, 51.158333)]],
'type': 'Polygon'},
'id': '1',
'properties': OrderedDict([('CAT', 232.0), ('FIPS_CNTRY', 'UK'), ('CNTRY_NAME', 'United Kingdom'), ('AREA', 244820.0), ('POP_CNTRY', 60270708.0)]),
'type': 'Feature'}
请注意,这些索引由gdal控制,并不总是遵循Python约定。它们可以从0、1(例如地理包)甚至其他值开始,并且不保证相邻。只有索引从0开始并且是连续的,负索引才能正常工作。
1.3.2 关闭文件¶
A Collection
涉及外部资源。除非你明确表示,否则不能保证这些会被释放。 close()
对象或使用 with
声明。当一个 Collection
是一个上下文保护,无论块内发生什么,它都是关闭的。
>>> try:
... with fiona.open('docs/data/test_uk.shp') as c:
... print(len(list(c)))
... assert True is False
... except:
... print(c.closed)
... raise
...
48
True
Traceback (most recent call last):
...
AssertionError
在中引发异常 with
上面的块,但从打印语句中可以看到 except
条款 c.__exit__()
(从而) c.close()
)已被调用。
重要
总是用:py:meth:~fiona.collection.Collection.close 或使用 with
而且,您永远不会被捆绑的外部资源、锁定的文件等绊倒。
1.4 格式化驱动程序、CRS、边界和架构¶
除了这些属性 file
( name
, mode
, closed
a) Collection
有只读的 driver
命名的属性 OGR format driver 用于打开矢量文件。
>>> c = fiona.open('docs/data/test_uk.shp')
>>> c.driver
'ESRI Shapefile'
这个 coordinate reference system 通过只读方式访问集合矢量数据的(CRS) crs
属性。
>>> c.crs
{'no_defs': True, 'ellps': 'WGS84', 'datum': 'WGS84', 'proj': 'longlat'}
CRS由以下映射表示: PROJ.4 参数。
这个 fiona.crs
模块提供了3个功能来帮助进行这些映射. to_string()
将转换为PROJ.4字符串的映射:
>>> from fiona.crs import to_string
>>> print(to_string(c.crs))
+datum=WGS84 +ellps=WGS84 +no_defs +proj=longlat
from_string()
是相反的。
>>> from fiona.crs import from_string
>>> from_string("+datum=WGS84 +ellps=WGS84 +no_defs +proj=longlat")
{'no_defs': True, 'ellps': 'WGS84', 'datum': 'WGS84', 'proj': 'longlat'}
from_epsg()
是从EPSG代码到CRS映射的快捷方式。
>>> from fiona.crs import from_epsg
>>> from_epsg(3857)
{'init': 'epsg:3857', 'no_defs': True}
没有验证
两个 from_epsg()
和 from_string()
简单地重新构造数据,它们不能确保生成的映射是预先定义的或以任何方式有效的CRS。
集合文件中的记录数可以通过python内置的 len()
功能。
>>> len(c)
48
这个 minimum bounding rectangle (MBR)或 bounds 通过只读方式获取集合的记录 bounds
属性。
>>> c.bounds
(-8.621389, 49.911659, 1.749444, 60.844444)
最后,通过只读访问其记录类型(记住矢量文件只有一种记录类型)的模式。 schema
属性。它有“geometry”和“properties”项。前者是一个字符串,后者是一个有序的dict,其中项的顺序与数据文件中的字段相同。
>>> import pprint
>>> pprint.pprint(c.schema)
{'geometry': 'Polygon',
'properties': {'CAT': 'float:16',
'FIPS_CNTRY': 'str',
'CNTRY_NAME': 'str',
'AREA': 'float:15.2',
'POP_CNTRY': 'float:15.2'}}
1.4.1 保持简单模式¶
Fiona在记录类型和模式方面采用的方法较少。关于记录类型的数据与关于记录的数据结构尽可能接近。模块化记录的“id”键,模式映射的键与集合记录映射的键相同。
>>> rec = next(c)
>>> set(rec.keys()) - set(c.schema.keys())
{'id'}
>>> set(rec['properties'].keys()) == set(c.schema['properties'].keys())
True
模式映射的值可以是附加映射,也可以是字段类型名称,如“polygon”、“float”和“str”。相应的python类型可以在名为 fiona.FIELD_TYPES_MAP
.
>>> pprint.pprint(fiona.FIELD_TYPES_MAP)
{'date': <class 'fiona.rfc3339.FionaDateType'>,
'datetime': <class 'fiona.rfc3339.FionaDateTimeType'>,
'float': <class 'float'>,
'int': <class 'int'>,
'str': <class 'str'>,
'time': <class 'fiona.rfc3339.FionaTimeType'>}
1.4.2 字段类型¶
简而言之,类型及其名称尽可能接近Python(或Javascript)中的预期。从python3开始,“str”字段类型可能包含Unicode字符。
>>> type(rec['properties']['CNTRY_NAME'])
<class 'str'>
>>> c.schema['properties']['CNTRY_NAME']
'str'
>>> fiona.FIELD_TYPES_MAP[c.schema['properties']['CNTRY_NAME']]
<class 'str'>
字符串类型字段还可以指示其最大宽度。值“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')
<type 'int'>
>>> prop_type('float')
<type 'float'>
>>> prop_type('str:25')
<class 'str'>
1.4.3 几何类型¶
Fiona支持geojson中的几何类型及其三维变体。这意味着模式的几何项的值将是以下值之一:
点
LineString
多边形
MultiPoint
MultiLineString
MultiPolygon
GeometryCollection
3D Point
3D LineString
3D Polygon
3D MultiPoint
3D MultiLineString
3D MultiPolygon
3D GeometryCollection
最后七种类型,即3D类型,只适用于集合模式。特征的几何类型始终是前七种类型之一。例如,“3D点”集合始终具有几何类型为“点”的特征。这些几何图形的坐标将是(x,y,z)元组。
请注意,最常见的矢量数据格式之一esri的shapefile没有“multilistering”或“multipolygon”模式几何。但是,在其模式中指示“polygon”的shapefile可以生成“polygon”或“multipolygon”功能。
1.5 记录¶
从集合中获得的记录是一条 Python dict
结构与geojson特性完全相同。FIONA记录是自描述的;其字段的名称包含在数据结构中,并且字段中的值是为记录类型正确键入的。数字字段值是类型的实例 int
和 float
例如,不是字符串。
>>> pprint.pprint(rec)
{'geometry': {'coordinates': [[(-4.663611, 51.158333),
(-4.669168, 51.159439),
(-4.673334, 51.161385),
(-4.674445, 51.165276),
(-4.67139, 51.185272),
(-4.669445, 51.193054),
(-4.665556, 51.195),
(-4.65889, 51.195),
(-4.656389, 51.192215),
(-4.646389, 51.164444),
(-4.646945, 51.160828),
(-4.651668, 51.159439),
(-4.663611, 51.158333)]],
'type': 'Polygon'},
'id': '1',
'properties': {'CAT': 232.0,
'FIPS_CNTRY': 'UK',
'CNTRY_NAME': 'United Kingdom',
'AREA': 244820.0,
'POP_CNTRY': 60270708.0}}
记录数据没有引用 Collection
其来源或任何其他外部资源。它是完全独立和安全使用的任何方式。关闭集合根本不影响记录。
>>> c.close()
>>> rec['id']
'1'
1.5.1 记录ID¶
唱片有 id
关键。与GeoJSON规范一样,它的对应值是数据文件中唯一的字符串。
>>> c = fiona.open('docs/data/test_uk.shp')
>>> rec = next(c)
>>> rec['id']
'0'
OGR细节
在 OGR 模型,特征ID是长整数。因此,Fiona记录ID通常是整数记录索引的字符串表示。
1.5.2 记录属性¶
记录有 properties
关键。它的对应值是一个映射:精确的有序dict。属性映射的键与记录来自的集合架构中的属性映射的键相同(请参见上文)。
>>> pprint.pprint(rec['properties'])
{'CAT': 232.0,
'FIPS_CNTRY': 'UK',
'CNTRY_NAME': 'United Kingdom',
'AREA': 244820.0,
'POP_CNTRY': 60270708.0}
1.5.3 记录几何¶
记录有 geometry
关键。它的对应值是 type
和 coordinates
密钥。
>>> pprint.pprint(rec['geometry'])
{'coordinates': [[(0.899167, 51.357216),
(0.885278, 51.35833),
(0.7875, 51.369438),
(0.781111, 51.370552),
(0.766111, 51.375832),
(0.759444, 51.380829),
(0.745278, 51.39444),
(0.740833, 51.400276),
(0.735, 51.408333),
(0.740556, 51.429718),
(0.748889, 51.443604),
(0.760278, 51.444717),
(0.791111, 51.439995),
(0.892222, 51.421387),
(0.904167, 51.418884),
(0.908889, 51.416939),
(0.930555, 51.398888),
(0.936667, 51.393608),
(0.943889, 51.384995),
(0.9475, 51.378609),
(0.947778, 51.374718),
(0.946944, 51.371109),
(0.9425, 51.369164),
(0.904722, 51.358055),
(0.899167, 51.357216)]],
'type': 'Polygon'}
因为坐标只是元组,或者元组列表,或者元组列表,所以 type
告诉你如何解释它们。
类型 |
协调 |
---|---|
点 |
一个(x,y)元组 |
LineString |
(x,y)元组顶点列表 |
多边形 |
环的列表(每个环都是(x,y)元组的列表) |
MultiPoint |
点列表(每个点都是一个(x,y)元组) |
MultiLineString |
一个行列表(每个行都是(x,y)元组的列表) |
MultiPolygon |
多边形列表(见上文) |
与GeoJSON 格式一样,Fiona有北半球的“北向上”和笛卡尔的“x-y”偏差。元组中表示为 (x, y)
上面要么是(本初子午线的经度E,赤道的纬度N),要么是其他投影坐标系(东距、北距)。
Long-Lat,不是Lat-Long
尽管我们大多数人都说“lat, long”,Fiona的 x,y
总是向东,向北,这意味着 (long, lat)
. 经度第一,纬度第二,符合GeoJSON 格式规范。
1.5.4 点集理论与简单特征¶
在一个适当的、经过良好清理的矢量数据文件中,上面解释的几何映射是由以下部分组成的几何对象的表示: point sets . 以下
{"type": "LineString", "coordinates": [(0.0, 0.0), (0.0, 1.0)]}
不仅表示两点,而且表示沿长度1.0的直线的无穷多点的集合 (0.0, 0.0)
到 (0.0, 1.0)
. 在点集理论的应用中通常被称为 Simple Features Access [sfa]如果两个几何对象的点集相等,则它们是相等的,无论它们在python意义上是否相等。如果安装了shapely(实现简单功能访问),则可以通过验证以下内容在中看到这一点。
>>> 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
废数据
某些文件可能包含以下向量: invalid 从简单特征的角度来看,由于附属品(生产商端的质量控制不充分)、目的(“废”矢量保存到文件中进行特殊处理)或数字精度模型的差异(Fiona还不能处理固定精度模型)。Fiona不会嗅探或试图清理废的数据,所以要确保你的数据来源是有效的。
1.6 写入矢量数据¶
可以打开矢量文件以在模式 'a'
(附加)或模式 'w'
(写)中写入。
注意
原地“更新”模式 OGR 完全依赖于格式,因此Fiona不支持。
1.6.1 将数据附加到现有文件¶
让我们从最简单但不是最常见的用例开始,将新记录添加到现有文件中。在修改之前复制文件,并在下面的示例中提取适当的记录。
>>> with fiona.open('docs/data/test_uk.shp') as c:
... rec = next(c)
>>> rec['id'] = '-1'
>>> rec['properties']['CNTRY_NAME'] = 'Gondor'
>>> import os
>>> os.system("cp docs/data/test_uk.* /tmp")
0
坐标参考系已经定义了文件的格式和模式,因此只打开了两个参数作为读取,但是仅在 'a'
模式。新记录将使用 write()
方法写入。因此,文件的长度从48增长到49。
>>> with fiona.open('/tmp/test_uk.shp', 'a') as c:
... print(len(c))
... c.write(rec)
... print(len(c))
...
48
49
您所写的记录必须与文件的模式匹配(因为文件包含一种类型的记录,请记住)。如果没有,你将获取:py:class:ValueError 。
>>> with fiona.open('/tmp/test_uk.shp', 'a') as c:
... c.write({'properties': {'foo': 'bar'}})
...
Traceback (most recent call last):
...
ValueError: Record data not match collection schema
那么,记录ID呢?写入文件的记录的ID将被忽略,并替换为适合该文件的下一个值。如果你读了上面附件,
>>> with fiona.open('/tmp/test_uk.shp', 'a') as c:
... records = list(c)
>>> records[-1]['id']
'48'
>>> records[-1]['properties']['CNTRY_NAME']
'Gondor'
你会看到 '-1'
在记录时会被替换为 '48'
.
这个 write()
方法将单个记录写入集合的文件。对应的 writerecords()
写入记录序列(或迭代器)。
>>> with fiona.open('/tmp/test_uk.shp', 'a') as c:
... c.writerecords([rec, rec, rec])
... print(len(c))
...
52
复制
Fiona 的集合不能防止复制。上面的代码将向文件中写入3个重复的记录,它们将被赋予唯一的顺序ID。
交易
菲奥娜在写操作期间使用事务以确保数据完整性。 writerecords()
将启动并提交一个事务。如果有很多记录,中间提交将以合理的间隔执行。
根据驱动程序的不同,事务可能是一个非常昂贵的操作。因为 write()
这只是一个方便的包装 writerecords()
对于单个记录,如果使用此方法逐个编写许多特性,则可能会遇到严重的性能问题。考虑先准备数据,然后在单个调用中写入数据 writerecords()
.
缓冲
Fiona的输出被缓冲。传递给 write()
和 writerecords()
的记录在集合关闭时刷新到磁盘。你也可以使用 flush()
定期将缓冲区内容写入磁盘。
1.6.2 创建相同结构的文件¶
写一个新文件比附加到现有文件要复杂得多,因为文件CRS、格式和模式还没有定义,必须由程序员来定义。不过,这并不复杂。模式只是一个映射,如上所述。CRS也只是一个映射,可能的格式在 fiona.supported_drivers
列表中枚举。
查看演示文件的参数。
>>> with fiona.open('docs/data/test_uk.shp') as source:
... source_driver = source.driver
... source_crs = source.crs
... source_schema = source.schema
...
>>> source_driver
'ESRI Shapefile'
>>> source_crs
{'no_defs': True, 'ellps': 'WGS84', 'datum': 'WGS84', 'proj': 'longlat'}
>>> pprint.pprint(source_schema)
{'geometry': 'Polygon',
'properties': {'CAT': 'float:16',
'FIPS_CNTRY': 'str',
'CNTRY_NAME': 'str',
'AREA': 'float:15.2',
'POP_CNTRY': 'float:15.2'}}
我们可以使用它们创建一个新文件。
>>> with fiona.open(
... '/tmp/foo.shp',
... 'w',
... driver=source_driver,
... crs=source_crs,
... schema=source_schema) as c:
... print(len(c))
... c.write(rec)
... print(len(c))
...
0
1
>>> c.closed
True
>>> len(c)
1
因为源架构的属性是按顺序排列的,并且以相同的顺序传递给写入模式集合,所以写入文件的字段与源文件的字段具有相同的顺序。
$ ogrinfo /tmp/foo.shp foo -so
INFO: Open of `/tmp/foo.shp'
using driver `ESRI Shapefile' successful.
Layer name: foo
Geometry: 3D Polygon
Feature Count: 1
Extent: (0.735000, 51.357216) - (0.947778, 51.444717)
Layer SRS WKT:
GEOGCS["GCS_WGS_1984",
DATUM["WGS_1984",
SPHEROID["WGS_84",6378137,298.257223563]],
PRIMEM["Greenwich",0],
UNIT["Degree",0.017453292519943295]]
CAT: Real (16.0)
FIPS_CNTRY: String (80.0)
CNTRY_NAME: String (80.0)
AREA: Real (15.2)
POP_CNTRY: Real (15.2)
这个 meta
属性使得文件元属性的复制更加容易。
>>> source = fiona.open('docs/data/test_uk.shp')
>>> sink = fiona.open('/tmp/foo.shp', 'w', **source.meta)
1.6.3 从头开始写入新文件¶
要从头开始编写新文件,我们必须定义自己的特定驱动程序、CRS和模式。
为了确保属性字段的顺序是可预测的,在作为特征属性的模式和实际表现形式中,我们将使用有序字典。
>>> from collections import OrderedDict
考虑以下记录,按照 Python geo protocol ,表示埃菲尔铁塔,使用点几何图形和31N区的UTM坐标。
>>> eiffel_tower = {
... 'geometry': {
... 'type': 'Point',
... 'coordinates': (448252, 5411935)
... },
... 'properties': OrderedDict([
... ('name', 'Eiffel Tower'),
... ('height', 300.01),
... ('view', 'scenic'),
... ('year', 1889)
... ])
... }
相应的方案可以是:
>>> landmarks_schema = {
... 'geometry': 'Point',
... 'properties': OrderedDict([
... ('name', 'str'),
... ('height', 'float'),
... ('view', 'str'),
... ('year', 'int')
... ])
... }
这些地标坐标的坐标参考系是ETRS89/UTM 31N区,在EPSG数据库中被引用为EPSG:25831。
>>> from fiona.crs import from_epsg
>>> landmarks_crs = from_epsg(25831)
合适的驱动程序可以是:
>>> output_driver = "GeoJSON"
有了指定模式、 crs和驱动程序后,我们准备打开一个文件来写入记录:
>>> with fiona.open(
... '/tmp/foo.geojson',
... 'w',
... driver=output_driver,
... crs=landmarks_crs,
... schema=landmarks_schema) as c:
... c.write(eiffel_tower)
...
>>> import pprint
>>> with fiona.open('/tmp/foo.geojson') as source:
... for record in source:
... pprint.pprint(record)
{'geometry': {'coordinates': (448252.0, 5411935.0), 'type': 'Point'},
'id': '0',
'properties': OrderedDict([('name', 'Eiffel Tower'),
('height', 300.01),
('view', 'scenic'),
('year', 1889)]),
'type': 'Feature'}
1.6.3.1 排序记录字段¶
从Fiona 1.0.1版开始, fiona.open()
's'schema'关键字参数可以是有序的dict或(key,value)对的列表,指定携带到写入文件中的顺序。如果给出了一个普通的dict,则顺序由该dict的 :py:func:`~items`输出决定.
例如,因为
>>> {'bar': 'int', 'foo': 'str'}.keys()
['foo', 'bar']
{{'properties': {{'bar': 'int', 'foo': 'str'}}}}
的模式将生成一个shapefile,其中第一个字段为“foo”,第二个字段为“bar”。如果你希望“bar”是第一个字段,则必须使用属性项列表
c = fiona.open(
"/tmp/file.shp",
"w",
schema={"properties": [("bar", "int"), ("foo", "str")], ...},
...,
)
或是有秩序的口述。
from collections import OrderedDict
schema_props = OrderedDict([("bar", "int"), ("foo", "str")])
c = fiona.open("/tmp/file.shp", "w", schema={"properties": schema_props, ...}, ...)
1.6.4 三维坐标和几何图形类型¶
如果将具有(x,y,z)元组的三维坐标写入二维文件(例如“点”模式几何图形),则Z值将丢失。
schema_props = OrderedDict([("foo", "str")])
feature = {
"geometry": {"type": "Point", "coordinates": (-1, 1, 5)},
"properties": OrderedDict([("foo", "bar")]),
}
with fiona.open(
"/tmp/file.shp",
"w",
driver="ESRI Shapefile",
schema={"geometry": "Point", "properties": schema_props},
) as collection:
collection.write(feature)
with fiona.open("/tmp/file.shp") as collection:
print(next(collection)["geometry"])
# {"type": "Point", "coordinates": (-1.0, 1.0)}
如果将只有(x,y)元组的二维坐标写入三维文件(例如'3D Point' 模式几何图形),z值将默认为0。
feature = {
"geometry": {"type": "Point", "coordinates": (-1, 1)},
"properties": OrderedDict([("foo", "bar")]),
}
with fiona.open(
"/tmp/file.shp",
"w",
driver="ESRI Shapefile",
schema={"geometry": "3D Point", "properties": schema_props},
) as collection:
collection.write(feature)
with fiona.open("/tmp/file.shp") as collection:
print(next(collection)["geometry"])
# {"type": "Point", "coordinates": (-1.0, 1.0, 0.0)}
1.7 高级主题¶
1.7.1 OGR配置选项¶
GDAL/OGR具有大量由全局或线程本地配置选项控制的功能。Fiona允许您使用上下文管理器配置这些选项, fiona.Env
. 此类的构造函数将GDAL/OGR配置选项作为关键字参数。例如,要查看来自GDAL/OGR的调试信息,可以执行以下操作。
import logging
import fiona
logging.basicConfig(level=logging.DEBUG)
with fiona.Env(CPL_DEBUG=True):
fiona.open("tests/data/coutwildrnp.shp")
以下额外消息将显示在python记录器的输出中.::
DEBUG:fiona._env:CPLE_None in GNM: GNMRegisterAllInternal
DEBUG:fiona._env:CPLE_None in GNM: RegisterGNMFile
DEBUG:fiona._env:CPLE_None in GNM: RegisterGNMdatabase
DEBUG:fiona._env:CPLE_None in GNM: GNMRegisterAllInternal
DEBUG:fiona._env:CPLE_None in GNM: RegisterGNMFile
DEBUG:fiona._env:CPLE_None in GNM: RegisterGNMdatabase
DEBUG:fiona._env:CPLE_None in GDAL: GDALOpen(tests/data/coutwildrnp.shp, this=0x1683930) succeeds as ESRI Shapefile.
如果你需要没有 Env
环境的 ``fiona.open()``时,将为您创建一个。
当您的程序退出带有块的环境时,配置将恢复到以前的状态。
1.7.2 云存储凭据¶
fiona.Env
最重要的用途之一就是设置用于访问存储在AWS S3或其他云存储系统中的数据的凭据。
from fiona.session import AWSSession
import fiona
with fiona.Env(
session=AWSSession(aws_access_key_id="key", aws_secret_access_key="secret")
):
fiona.open("zip+s3://example-bucket/example.zip")
AWSSession类当前是Fiona中唯一的凭据会话管理器。源代码有一个示例,说明如何实现其他云存储提供程序的类。AWSSession依赖于boto3和botocore,如果您运行 pip install fiona[s3]
,它们将作为Fiona的额外依赖项安装 .
如果你需要没有周围环境 Env
的``fiona.open()``并将路径传递给S3对象时,将使用与以下代码等效的代码为您创建会话。
import boto3
from fiona.session import AWSSession
import fiona
with fiona.Env(session=AWSSession(boto3.Session())):
fiona.open("zip+s3://fiona-testing/coutwildrnp.zip")
1.7.3 切片和屏蔽迭代器¶
对于一些矢量数据格式,空间索引伴随着数据文件,允许有效的边界框搜索。一个集合的 items()
方法返回一个迭代器,该迭代器位于与给定的 (minx, miny, maxx, maxy)
边界框或几何体对象。这个集合自身的坐标参考系(见下文)用于解释框的值。如果需要迭代器项的列表,请将其传递给python的builtin list()
如下所示。
>>> c = fiona.open('docs/data/test_uk.shp')
>>> hits = list(c.items(bbox=(-5.0, 55.0, 0.0, 60.0)))
>>> len(hits)
7
迭代器方法采用相同的 stop
或 start, stop[, step]
参数切片为 itertools.islice()
. 要从迭代器中仅获取前两个项,就传递一个停止索引。
>>> hits = c.items(2, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
2
要从迭代器中获取第三到第五项,请传递开始和停止索引。
>>> hits = c.items(2, 5, bbox=(-5.0, 55.0, 0.0, 60.0))
>>> len(list(hits))
3
要按属性值过功能,请使用python的内置 filter()
和 lambda
或者您自己可以接受一个特征记录并返回 True
或 ``False``的过滤器函数.
>>> def pass_positive_area(rec):
... return rec['properties'].get('AREA', 0.0) > 0.0
...
>>> c = fiona.open('docs/data/test_uk.shp')
>>> hits = filter(pass_positive_area, c)
>>> len(list(hits))
48
1.7.4 读取多层数据¶
到目前为止,只显示了每个文件具有一个主题层或特征类型的简单数据集,而古老的ESRI形状文件是主要示例。其他GIS数据格式可以在单个文件或目录中对多个图层或要素类型进行编码。Esri'的 File Geodatabase 就是这种格式的一个例子。在本手册中,一个更有用的示例是包含多个形状文件的目录。下面的三个shell命令将从使用fiona分发的测试数据创建这样的两层数据源。
$ mkdir /tmp/data
$ ogr2ogr /tmp/data/ docs/data/test_uk.shp test_uk -nln foo
$ ogr2ogr /tmp/data/ docs/data/test_uk.shp test_uk -nln bar
数据源的层可以使用 fiona.listlayers()
列出.在shapefile格式的情况下,层名称与文件的基本名称匹配。
>>> fiona.listlayers('/tmp/data')
['bar', 'foo']
与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)
...
{'geometry': 'Polygon',
'properties': {'CAT': 'float:16',
'FIPS_CNTRY': 'str',
'CNTRY_NAME': 'str',
'AREA': 'float:15.2',
'POP_CNTRY': 'float:15.2'}}
{'geometry': 'Polygon',
'properties': {'CAT': 'float:16',
'FIPS_CNTRY': 'str',
'CNTRY_NAME': 'str',
'AREA': 'float:15.2',
'POP_CNTRY': 'float:15.2'}}
层也可以通过其索引来指定。
>>> for i, name in enumerate(fiona.listlayers(datasrc_path)):
... with fiona.open(datasrc_path, layer=i) as c:
... print(len(c))
...
48
48
如果没有指定图层, fiona.open()
使用第一层返回打开的集合。
>>> with fiona.open(datasrc_path) as c:
... c.name == fiona.listlayers(datasrc_path)[0]
...
True
使用以下所有参数打开形状文件进行读取的最常用方法 fiona.open()
,将其视为具有命名层的数据源。
>>> fiona.open('docs/data/test_uk.shp', 'r', layer='test_uk')
在实践中,可以依靠隐式的第一层和默认层 'r'
模式并打开这样的形状文件:
>>> fiona.open('docs/data/test_uk.shp')
1.7.5 写入多层数据¶
要将一个全新的图层写入多层数据源,只需为 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)
['bar', 'foo', 'wah']
在 '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:
... # Overwrites the existing layer named 'wah'!
1.7.6 虚拟文件系统¶
Zip和tar归档文件可以被视为虚拟文件系统,并且可以从其中的路径和层进行收集。换句话说,fiona允许您读取压缩形状文件。例如,从与fiona一起分发的shapefile创建一个zip存档。
$ zip /tmp/zed.zip docs/data/test_uk.*
adding: docs/data/test_uk.shp (deflated 48%)
adding: docs/data/test_uk.shx (deflated 37%)
adding: docs/data/test_uk.dbf (deflated 98%)
adding: docs/data/test_uk.prj (deflated 15%)
fiona.listlayers()
和 fiona.open()
的`vfs`关键字参数可能是以“zip://”或“tar://”开头的 Apache Commons VFS 样式的字符串,后跟指向存档文件的绝对或相对路径。使用此参数时,第一个参数必须是该存档中的绝对路径。该zip存档中的层是:
>>> import fiona
>>> fiona.listlayers('/docs/data', vfs='zip:///tmp/zed.zip')
['test_uk']
也可以这样访问单个形状文件:
>>> with fiona.open(
... '/docs/data/test_uk.shp',
... vfs='zip:///tmp/zed.zip') as c:
... print(len(c))
...
48
1.7.7 MemoryFile和ZipMemoryFile¶
fiona.io.MemoryFile`和 :py:class:`fiona.io.ZipMemoryFile
允许在内存中读取或写入格式化的功能集合,甚至是压缩的功能集合,而无需访问文件系统。例如,您可能有一个字节流来自Web上载或下载的压缩形状文件。
>>> data = open('tests/data/coutwildrnp.zip', 'rb').read()
>>> len(data)
154006
>>> data[:20]
b'PK\x03\x04\x14\x00\x00\x00\x00\x00\xaa~VM\xech\xae\x1e\xec\xab'
可以通过将其包装在ZipMemoryFile的实例中来访问此字节流中的功能集合。
>>> from fiona.io import ZipMemoryFile
>>> with ZipMemoryFile(data) as zip:
... with zip.open('coutwildrnp.shp') as collection:
... print(len(collection))
... print(collection.schema)
...
67
{'properties': OrderedDict([('PERIMETER', 'float:24.15'), ('FEATURE2', 'str:80'), ('NAME', 'str:80'), ('FEATURE1', 'str:80'), ('URL', 'str:101'), ('AGBUR', 'str:80'), ('AREA', 'float:24.15'), ('STATE_FIPS', 'str:80'), ('WILDRNP020', 'int:10'), ('STATE', 'str:80')]), 'geometry': 'Polygon'}
1.8.0版中的新功能*
1.8 FIONA命令行接口¶
fiona附带一个名为“fio”的命令行接口。查看 CLI Documentation 详细使用说明。
1.9 最终笔记¶
本手册正在使用且将随着Fiona的发展而不断完善。欢迎提出问题和建议。请放心使用 issue tracker 或者直接给作者发电子邮件。
请参阅 README 有关支持的Python版本和其他软件依赖项的安装说明和信息。
如果没有 contributions of other developers,Fiona是不可能实现的,尤其是GDAL/OGR的开发者Frank Warmerdam和Even Rouault,还有把Fiona从忽视和默默无闻中拯救了出来的Mike Weisman。