作者: | Sean Gillies, <sean.gillies@gmail.com> |
---|---|
版本: | 1.8.4 |
日期: | 2019 年 03 月 23 日 |
版权: | 这项工作是根据 Creative Commons Attribution 3.0 United States License. |
摘要: | Fiona是OGR的简洁,灵活,严谨的API。本文档介绍如何使用Fiona软件包读取和写入地理空间数据文件。在示例中使用了python 3。参见 README 用于安装和快速启动说明。 |
---|
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文档上。
在哪些情况下,您会从使用 Fiona 中受益?
在哪些情况下,您不会从使用 Fiona 中受益?
json
或 simplejson
模块。SQLAlchemy
或 GeoAlchemy
. 也许你在用 GeoDjango
已经。如果是,继续。httplib2
, Requests
等)或提供程序的python api。使用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.
在地理信息系统中,离散的地理特征通常用 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的数据访问。
读取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 就像一条巨蟒 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版新增的功能。
集合的特性也可以通过索引访问。
>>> 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([(u'CAT', 232.0), (u'FIPS_CNTRY', u'UK'), (u'CNTRY_NAME', u'United Kingdom'), (u'AREA', 244820.0), (u'POP_CNTRY', 60270708.0)]),
'type': 'Feature'}
请注意,这些索引由gdal控制,并不总是遵循Python约定。它们可以从0、1(例如地理包)甚至其他值开始,并且不保证相邻。只有索引从0开始并且是连续的,负索引才能正常工作。
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
而且,您永远不会被捆绑的外部资源、锁定的文件等绊倒。
除了这些属性 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}
集合文件中的记录数可以通过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'}}
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'>}
简而言之,类型及其名称尽可能接近您在Python(或JavaScript)中所期望的。“str”和“unicode”混乱是python<3.0中的一个事实。Fiona记录有unicode字符串,但它们的字段类型名称是“str”(期待使用python 3)。
>>> 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'>
上面的示例是针对Python3的。对于python 2,“str”属性的类型为“unicode”。
>>> prop_type('str:25')
<class 'unicode'>
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”功能。
从集合中获得的记录是一条 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'
唱片有 id
关键。与GeoJSON规范一样,它的对应值是数据文件中唯一的字符串。
>>> c = fiona.open('docs/data/test_uk.shp')
>>> rec = next(c)
>>> rec['id']
'0'
OGR细节
在 OGR 模型,特征ID是长整数。因此,Fiona记录ID通常是整数记录索引的字符串表示。
记录有 properties
关键。它的对应值是一个映射:精确的有序dict。属性映射的键与记录来自的集合架构中的属性映射的键相同(请参见上文)。
>>> pprint.pprint(rec['properties'])
{'CAT': 232.0,
'FIPS_CNTRY': 'UK',
'CNTRY_NAME': 'United Kingdom',
'AREA': 244820.0,
'POP_CNTRY': 60270708.0}
记录有 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 格式规范。
在一个适当的、经过良好清理的矢量数据文件中,上面解释的几何映射是由以下部分组成的几何对象的表示: 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不会嗅探或试图清理废的数据,所以要确保你的数据来源是有效的。
可以打开矢量文件以在模式 'a'
(附加)或模式 'w'
(写)中写入。
注意
原地“更新”模式 OGR 完全依赖于格式,因此Fiona不支持。
让我们从最简单但不是最常见的用例开始,将新记录添加到现有文件中。在修改之前复制文件,并在下面的示例中提取适当的记录。
>>> 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。
缓冲
Fiona的输出被缓冲。传递给 write()
和 writerecords()
的记录在集合关闭时刷新到磁盘。你也可以使用 flush()
定期将缓冲区内容写入磁盘。
写一个新文件比附加到现有文件要复杂得多,因为文件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)
要从头开始编写新文件,我们必须定义自己的特定驱动程序、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'}
从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, ...},
... )
如果将具有(x,y,z)元组的三维坐标写入二维文件(例如“点”模式几何图形),则Z值将丢失。
如果将只有(x,y)元组的二维坐标写入三维文件(例如'3D Point' 模式几何图形),z值将默认为0。
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()``时,将为您创建一个。
当程序退出带有块的环境时,配置将恢复到以前的状态。
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')
对于一些矢量数据格式,空间索引伴随着数据文件,允许有效的边界框搜索。一个集合的 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
到目前为止,只显示了每个文件具有一个主题层或特征类型的简单数据集,而古老的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')
要将一个全新的图层写入多层数据源,只需为 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'!
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
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版中的新功能*
Fiona安装一个名为 dumpgj
. 它将文件转换为json-ld上下文作为选项的GeoJSON,并打算升级为“ogr2ogr-f geojson”。
$ dumpgj --help
usage: dumpgj [-h] [-d] [-n N] [--compact] [--encoding ENC]
[--record-buffered] [--ignore-errors] [--use-ld-context]
[--add-ld-context-item TERM=URI]
infile [outfile]
Serialize a file's records or description to GeoJSON
positional arguments:
infile input file name
outfile output file name, defaults to stdout if omitted
optional arguments:
-h, --help show this help message and exit
-d, --description serialize file's data description (schema) only
-n N, --indent N indentation level in N number of chars
--compact use compact separators (',', ':')
--encoding ENC Specify encoding of the input file
--record-buffered Economical buffering of writes at record, not
collection (default), level
--ignore-errors log errors but do not stop serialization
--use-ld-context add a JSON-LD context to JSON output
--add-ld-context-item TERM=URI
map a term to a URI and add it to the output's JSON LD
context
本手册正在使用且将随着Fiona的发展而不断完善。欢迎提出问题和建议。请放心使用 issue tracker 或者直接给作者发电子邮件。
请参阅 README 有关支持的Python版本和其他软件依赖项的安装说明和信息。
如果没有 contributions of other developers,Fiona是不可能实现的,尤其是GDAL/OGR的开发者Frank Warmerdam和Even Rouault,还有把Fiona从忽视和默默无闻中拯救了出来的Mike Weisman。
[Kent1978] | 威廉·肯特,《数据与现实》,北荷兰,1978年。 |
[ESRI1998] | ESRI形状文件技术说明。1998年7月。http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf |
[GeoJSON] | http://geojson.org |
[JSON] | http://www.ietf.org/rfc/rfc4627 |
[SFA] | http://en.wikipedia.org/wiki/Simple_feature_access |