Python中使用面狀矢量裁剪柵格影像,並依據Value值更改矢量屬性


本文整體思路:在Python中使用Geopandas庫,依次讀取shp文件的每一個面狀要素,獲取其空間邊界信息並裁剪對應的柵格影像,計算所裁剪影像Value值的眾數,將其設置為對應面狀要素的NewTYPE值,所有要素屬性值都改好之后保存為新的shp文件。

使用Python處理空間數據確實用的不多,所以一個星期以來一直深受這個程序的折磨,官方文檔、博客、谷歌、百度、論文,能用的方法都給用了,但是進度還是很慢,特別是當看到這篇博客的時候。。。好氣啊。。

不過幸虧頭比較鐵,雖敗不餒,慢慢一步一步調試找問題,一個一個解決,終於啃下了這根硬骨頭。(對寫程序來說,調試真的是第一法寶啊!)

好吧進入正題。。。

Pandas是一款高性能的Python數據分析庫,而Geopandas是由Shapely、Fiona、PyProj、matplotlib以及其他必須的庫共同構建的Pandas地理空間擴展,它既可以處理空間數據、也可以處理屬性數據。看到有些博客說在讀取shp文件的時候用Geopandas庫,而在編輯、導出的時候用pyshp比較好,其實不然,Geopandas也提供了功能完備的導出接口,而且使用特別方便,只不過需要注意一個小的細節問題,否則就會報錯。Rasterio是基於GDAL庫二次封裝的主要用於空間柵格數據處理的Python庫,本程序需要對柵格影像進行裁剪,因此也需要引用這個庫。兩個庫的官方文檔如下,參考的時候要注意版本問題,不同的版本有些接口可能已經改變。Geopandas參考文檔Rasterio參考文檔

我是Windows 10系統,在Python中安裝Geopandas庫比較麻煩,不能用pip命令直接安裝,而需要先下載Anaconda再用conda命令安裝,這部分網上有很多參考資料,就不多贅述了。但是安裝完成之后發現它的一些依賴庫不能使用,需要pip命令將其卸載之后,再通過此地址:python依賴庫下載對應的依賴庫並安裝就可以使用了。

本程序完整的代碼如下:

# -*- coding: utf-8 -*-
from geopandas import *;
import rasterio as rio;
import rasterio.mask;

# 讀入矢量和柵格文件
shpdatafile='D:/PythonConda/Data/ShpData.shp'
rasterfile='D:/PythonConda/Data/Raster.tif'
out_file='D:/PythonConda/Data/OutShpData'
shpdata=GeoDataFrame.from_file(shpdatafile)
rasterdata=rio.open(rasterfile)

out_shpdata = shpdata.copy()
#投影變換,使矢量數據與柵格數據投影參數一致
shpdata=shpdata.to_crs(rasterdata.crs)

for i in range(0, len(shpdata)):
    # 獲取矢量數據的features
    geo = shpdata.geometry[i]
    feature = [geo.__geo_interface__]
    #通過feature裁剪柵格影像
    out_image, out_transform = rio.mask.mask(rasterdata, feature, all_touched=True, crop=True, nodata=rasterdata.nodata)
    #獲取影像Value值,並轉化為list
    out_list = out_image.data.tolist()
    #除去list中的Nodata值
    out_list = out_list[0]
    out_data = []
    for k in range(len(out_list)):
        for j in range(len(out_list[k])):
            if out_list[k][j] >=0:
                out_data.append(out_list[k][j])
    #求數據中的眾數
    if len(out_data):
        counts = np.bincount(out_data)
        new_type = np.argmax(counts)
    else:
        new_type = None
    #依據眾數值更改feature的NewTYPE屬性值
    out_shpdata.NewTYPE[i] = new_type

#將屬性更改過的GeodataFrame導出為shp文件
out_shpdata.to_file(out_file)

需要注意的問題:

1)兩個文件需要在同一坐標系下,需要將用於裁剪的shp數據進行投影變換,將其投影參數變為柵格數據的投影參數,由以下代碼實現:

shpdata=shpdata.to_crs(rasterdata.crs)

其中crs表示數據的投影參數,to_crs為投影參數轉換函數。

2)裁剪函數rasterio.mask.mask的參數問題,需要傳入的矢量數據為GeoJSON數據,因此讀入的每一個面狀feature都需要用__geo_interface__函數進行格式轉換,這一步可以通過調試來查看具體的數據格式是否正確;此函數有兩個返回值,第一個記錄裁剪柵格的數據值,第二個記錄其坐標轉換信息,一般用到第一個返回值比較多;裁剪得到的柵格形狀其實是一個矩形,與feature的外接矩形區域一致,只是位於feature外部的像素值默認被設置為Nodata,當然這可以通過傳入的參數進行設置。

3)獲取到裁剪的柵格后,通過.data來獲取其Value值,但此時還不能直接用於統計分析,需要將數據轉化為List,函數如下:

out_list = out_image.data.tolist()

此時還需要調試來進一步確定out_list的數據內容,發現out_list[0]才是我們真正能用到的數據值。

4)獲取眾數的時候需要清除Nodata值的影響,因此用for循環把out_list中的非Nodata數據再組成一個新的List,用numpy的自帶函數求其眾數。因為所有的編輯並不能對shpdata源數據進行改變,所以需要構建一個shpdata的copy即out_shpdata,將求得的眾數賦給out_shpdata的NewTYPE,編輯完成之后再將out_shpdata導出為完整的shp文件。

5)GeoDataFrame.to_file函數可以將out_shpdata直接導出為shp文件,僅需要傳入一個路徑參數即可,但需要注意由於shp文件包含.shp、.shx、.dbf和.prj,因此路徑只能是一個文件夾,而不能具體到.shp。如下代碼所示:

out_file='D:/PythonConda/Data/OutShpData'

 最后將生成的數據在Arcmap中打開,設置顯示的Labels后可以看到效果如下:

至此全部完成!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM