Python3.GDAL從shp文件生成mask

沒有ArcGIS的矢量轉柵格工具的時候如何用shp多邊形從柵格數據中摳出一塊來?

from osgeo import gdal
result = gdal.Warp('masked.tif', 'input.tif', cutlineDSName='input.shp')
result.FlushCache()
del result

BOOM!完成!input.tif 被 input.shp 摳出來的部分就保存爲了 masked.tif

你以爲這就完了嗎?

I’ll do you one better!

在這裏插入圖片描述
有時候有沒有覺得想要一個和你的柵格數據同形的掩膜mask?

舉個例子:

比如要分析大量柵格數據中固定感興趣區域的某個統計量,原來每個柵格都要扣一遍生成一個扣過的文件再分析,如果有了感興趣區域的mask只要讀取每個柵格再mask一下數據就出來了。通常mask比用分辨率很高的shp扣要快,而且這個mask可以反覆用反覆節約時間、硬盤空間。

我寫成了函數可以直接調用:

def shp2mask(shp_name, description, mask_name='mask'):
    """從shp文件生成mask

    從shp_name的多邊形生成形如description的mask

    Args:
        shp_name: shapfile文件名
        description: (upper_left_x, upper_left_y, pixel_width, pixel_height, rows, cols)
        mask_name: 生成的mask文件名,支持.tif和.npy格式,否則兩種格式都生成

    Returns:
        None
    """
    (upper_left_x, upper_left_y, pixel_width, pixel_height, rows, cols) = description
    mem = gdal.GetDriverByName('MEM')
    mid_ds = mem.Create('', cols, rows, 1, gdal.GDT_Byte)
    mid_ds.SetGeoTransform([upper_left_x, pixel_width, 0, upper_left_y, 0, pixel_height])
    mid_ds.SetMetadataItem('AREA_OR_POINT', 'Point')
    mid_ds.GetRasterBand(1).WriteArray(np.ones((rows, cols), dtype=np.bool))
    srs = osr.SpatialReference()
    srs.SetWellKnownGeogCS('WGS84')
    mid_ds.SetProjection(srs.ExportToWkt())
    mask_ds = gdal.Warp('', mid_ds, format='MEM', cutlineDSName=shp_name)
    out_format = mask_name[-3:]
    if out_format == 'tif':
        ds2GTiff(mask_ds, mask_name)
    elif out_format == 'npy':
        ds2npy(mask_ds, mask_name)
    else:
        ds2GTiff(mask_ds, mask_name+'.tif')
        ds2npy(mask_ds, mask_name+'.npy')
    del mid_ds, mask_ds

def ds2GTiff(ds, tif_name):
    gtiff = gdal.GetDriverByName('GTiff')
    result = gtiff.CreateCopy(tif_name, ds)
    result.FlushCache()
    del result

def ds2npy(ds, npy_name):
    np.save(npy_name, ds.ReadAsArray().astype(np.bool), allow_pickle=False)

if __name__ == '__main__':
    shp_name = r'E:\data\map\cn1984\cn1984.shp'
    mask_name = r'mask.tif'
    description = (70, 60, 0.05, -0.05, 1201, 1401)
    shp2mask(shp_name, description, mask_name)

調用方法可以看最後4行,傳入你的shp_name,用含6個數字的列表描述想要的mask左上角的經緯度、水平垂直分辨率、行列數,指定輸出mask的文件名就行,可以輸出tif和npy格式的mask。

在這裏插入圖片描述

自己實現這個操作的動機是畫圖。前一段時間幾個羣裏總有人問如何“白化”,當時我還不知道啥叫白化,後來自己也實現了作圖過程中用set_clip_path方法白化,甚至也獨立嘗試出了同時白化多個區域的方法。實現後才發現氣象家園有個平流層的蔬菜早就做出來了,而且我和他拼接Path的想法與方法都一樣。

在文檔尚不十分豐富的情況下通過自己的理解充分發揮出這些代碼工具的真實威力,真的是人生一大快事,而發現前路有人更讓我熱血沸騰,帶酒趕路,在此分享出白化的另一種方法(構造np.ma.masked_array就行了)。

在這裏插入圖片描述

回過來說說這篇教程,shp文件裁剪柵格,非常基礎的操作,不知是不是我找得姿勢不對,網上尤其是中文網絡中並沒有用Python實現的介紹,只有幾篇藉助PIL庫和用Python調用系統命令操作GDAL的相關介紹,費了老鼻子勁兒才找到問題的核心gdal.Warp。另外值得吹一吹的是,我是讀網上一篇C++的GDAL教程才找到在內存中建立gdal.Dataset的方法(就是MEM相關的那段),這樣就減少了硬盤的讀寫,節約空間提高效率。

快,點贊誇我!


微信公衆號:碎積雲
CSDN:墨大寶 https://blog.csdn.net/modabao
GitHub:Mo-Dabao https://github.com/Mo-Dabao
知乎:墨大寶 https://www.zhihu.com/people/mo_dabao
氣象家園:墨家大寶 http://bbs.06climate.com/?86416

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章