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包( 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, 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、邊界和架構?

除了這些屬性 filename , 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記錄是自描述的;其字段的名稱包含在數據結構中,并且字段中的值是為記錄類型正確鍵入的。數字字段值是類型的實例 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