本文對應代碼和數據已上傳至我的
Github
倉庫https://github.com/CNFeffery/DataScienceStudyNotes
1 簡介
在上一篇文章中我們對geopandas
中的坐標參考系有了較為深入的學習,而在日常空間數據分析工作中矢量文件的讀入和寫出,是至關重要的環節。
作為基於geopandas的空間數據分析系列文章的第三篇,通過本文你將會學習到geopandas
中的文件IO。
2 文件IO
2.1 矢量文件的讀入
geopandas
將fiona
作為操縱矢量數據讀寫功能的后端,使用geopandas.read_file()
讀取對應類型文件,而在后端實際上是使用fiona.open
來讀入數據,即兩者參數是保持一致的,讀入的數據自動轉換為GeoDataFrame
,下面是geopandas.read_file()
主要參數:
filename:str類型,傳入文件對應的路徑或url
layer:str類型,當要讀入的數據格式為地理數據庫.gdb
或QGIS
中的.gpkg
時,傳入對應圖層的名稱
下面結合上述參數,來介紹一下使用geopandas.read_file()
在不同情況下讀取常見格式矢量數據的方法,使用到的示例數據為中國地圖,CRS
為EPSG:4326
,本文使用到的所有數據都可以在文章開頭提及的Github
倉庫對應本文路徑下找到:

2.1.1 shapefile
作為非常常見的一種矢量文件格式,geopandas
對shapefile
提供了很好的讀取和寫出支持,下面分為不同情況來介紹:
- 完整的shapefile
如圖2,這是一個完整的shapefile
:

使用geopandas
來讀取這種形式的shapefile
很簡單:
import geopandas as gpd
data = gpd.read_file('geometry/china_provinces/china_provinces.shp')
print(data.crs) # 查看數據對應的crs
data.head() # 查看前5行

- 缺少投影的shapefile
當shapefile
中缺失.prj
文件時,使用geopandas
讀入后形成的GeoDataFrame
會缺失crs
屬性:

如果已經知道數據對應的CRS
,可以在讀入數據后補充上crs
信息以進行其他操作:
import pyproj
data.crs = pyproj.CRS.from_user_input('EPSG:4326')
data.crs

- 直接讀取文件夾
當文件夾下只有單個shapefile時,可以直接讀取該文件夾:

- 讀取zip壓縮包中的文件
geopandas
通過傳入特定語法格式的文件路徑信息,以支持直接讀取.zip
格式壓縮包中的shapefile
文件,主要分為兩種情況。
當文件在壓縮包內的根目錄時,使用下面的語法規則來讀取數據:
zip://路徑/xxx.zip
譬如我們要讀取圖7所示的壓縮包內文件:

按照對應的語法規則,讀取該類型數據方式如下:

而當文件在壓縮包內的文件夾中時,如圖9:

使用下面的語法規則來讀取數據:
zip://路徑/xxx.zip!壓縮包內指定文件路徑
將上述語法運用到上述文件:

2.1.2 gdb與gpkg
對於Arcgis
中的地理數據庫gdb
,以及QGIS
中的GeoPackage
,要讀取其包含的矢量數據,就要涉及到圖層的概念,對應geopandas.read_file()
的layer
參數,只需要將gdb
或gpkg
文件路徑作為filename
參數,再將對應的圖層名稱作為layer
參數傳入:
- gdb
data = gpd.read_file('geometry/china_provinces.gdb',
layer='china_provinces')
print(data.crs) # 查看數據對應的crs
data.head() # 查看前5行

- gpkg
類似讀入gdb
文件:
data = gpd.read_file('geometry/china_provinces.gpkg',
layer='china_provinces',
encoding='utf-8')
print(data.crs) # 查看數據對應的crs
data.head() # 查看前5行

2.1.3 GeoJSON
作為web地圖中最常使用的矢量數據格式,GeoJSON
幾乎被所有在線地圖框架作為數據源格式,在geopandas
中讀取GeoJSON
非常簡單,只需要傳入文件路徑名稱即可,下面我們來讀入圖13所示的文件:


2.1.4 過濾
geopandas
在0.1.0版本中新增了bbox過濾,在0.7.0版本中新增了蒙版過濾和行過濾功能,可以輔助我們根據自己的需要讀入原始數據中的子集,下面一一進行介紹:
- bbox過濾
bbox過濾允許我們在read_file()
中傳入一個邊界框作為參數bbox
,格式為(左下角x, 左下角y, 右上角x, 右上角y)
,這樣在讀入的過程中只會保留幾何對象與bbox
有相交的數據記錄,下面我們仍然以上文中使用過的中國地圖數據為例,我們在讀入的過程中,傳入邊界框:
from shapely import geometry
data = gpd.read_file('geometry/china_provinces.json',
bbox=(100, 20, 110, 30))
%matplotlib widget
ax = data.plot()
# 繪制bbox框示意
ax = gpd.GeoSeries([geometry.box(minx=100,
miny=20,
maxx=110,
maxy=30).boundary]).plot(ax=ax, color='red')

可以看到只有跟紅色框有相交的幾何對象被讀入。
- 蒙版過濾
蒙版過濾和bbox過濾功能相似,都是篩選與指定區域相交的數據記錄,不同的是蒙版過濾通過mask
參數可以傳入任意形狀的多邊形,不再像bbox過濾那樣只接受矩形:
data = gpd.read_file('geometry/china_provinces.json',
mask=geometry.Polygon([(100, 20), (110, 30), (120, 20)]))
ax = data.plot()
# 繪制bbox框示意
ax = gpd.GeoSeries([geometry.Polygon([(100, 20),
(110, 30),
(120, 20)]).boundary]).plot(ax=ax, color='red')

可以看到只有跟紅色多邊形相交的幾何對象被讀入。
- 行過濾
行過濾的功能就比較簡單,通過參數rows
控制讀入原數據的前若干行,可以用於在讀取大型數據時先快速查看前幾行以了解整個數據的格式:

2.2 矢量文件的寫出
在geopandas
中使用to_file()
來將GeoDataFrame
或GeoSeries
寫出為矢量文件,主要支持shapefile
、GeoJSON
以及GeoPackage
,不像geopandas.read_file()
可以根據傳入的文件名稱信息自動推斷類型,我們在寫出矢量數據時就需要使用driver
參數來聲明文件類型:
- ESRI Shapefile
我們將上文最后一次讀入的GeoDataFrame
寫出為ESRI Shapefile
,設置driver
參數為ESRI Shapefile
,如果你對文件編碼有要求,這里可以使用encoding
參數來指定,譬如這里我們指定為utf-8
:
'''在工程根目錄下創建output文件夾'''
import os
try:
os.mkdir('output')
except FileExistsError:
pass
data.to_file('output/output.shp',
driver='ESRI Shapefile',
encoding='utf-8')
可以看到在output文件夾下,成功導出了完整的shapefile
:

而如果導出的文件名不加后綴擴展名,則會生成包含在新目錄下的shapefile
:
data.to_file('output/output_shapefile',
driver='ESRI Shapefile',
encoding='utf-8')

也可以向指定的文件夾下追加圖層:
data.to_file('output/output_shapefile_multi_layer',
driver='ESRI Shapefile',
layer='layer1',
encoding='utf-8')
data.to_file('output/output_shapefile_multi_layer',
driver='ESRI Shapefile',
layer='layer2',
encoding='utf-8')
data.to_file('output/output_shapefile_multi_layer',
driver='ESRI Shapefile',
layer='layer3',
encoding='utf-8')

- GeoPackage
對於gdb
文件,由於ESRI
的限制,暫時無法在開源的geopandas
中導出,但我們可以用QGIS
中的GeoPackage
作為替代方案(開源世界萬歲O(∩_∩)O~~),只需要將driver
參數設置為GPKG
即可,這里需要注意一個bug:在使用geopandas
導出GeoPackage
文件時,可能會出現圖21所示錯誤:

但我觀察到即使出現了上述錯誤,GeoPackage
文件也是成功保存到路徑下的且整個程序並未被打斷,因此可以無視上述錯誤:

- GeoJSON
寫出為GeoJSON
非常容易,只需要設置driver='GeoJSON'
即可:

以上就是本文的全部內容,如有筆誤望指出!