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包,如 Shapelypyproj 以及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的 jsonsimplejson 模块。

  • 如果您的数据位于像postgis这样的RDBMS中,请使用python-db包或类似orm的包。 SQLAlchemyGeoAlchemy . 也许你在用 GeoDjango 已经。如果是,继续。

  • 如果您的数据是通过来自couchdb或cartodb等的http提供的,请使用http包( httplib2Requests 等)或提供程序的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, LayersFeatures . 功能没有字段,而是属性和 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、边界和架构

除了这些属性 filenamemodeclosed 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记录是自描述的;其字段的名称包含在数据结构中,并且字段中的值是为记录类型正确键入的。数字字段值是类型的实例 intfloat 例如,不是字符串。

>>> 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 关键。它的对应值是 typecoordinates 密钥。

>>> 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

迭代器方法采用相同的 stopstart, 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。

1.10 参考

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