基于gma库计算1990-2022年吐哈盆地栅格SPEI

在本文中,我们使用SPEI作为干旱指数来分析吐哈盆地的干旱情况。SPEI是一种国际上通用的干旱指数,它融合了PDSI对蒸散发的响应和SPI的空间一致性及多时间尺度的优点。该指数目前已被广泛应用于气象、水文、农业和社会经济领域的干旱监测和预报研究。因此,本文基于彭守璋团队发布的1km逐月降水量数据和前文计算得到的ET0-PM数据,参考https://gma.luosgeo.com/UserGuide/climet/Index/SPEI.html,利用gma 2.0.2.1库计算1990-2022年吐哈盆地月尺度、季尺度、半年尺度和年尺度的SPEI。在计算四季的干旱情况时,我们参考前文中的季节划分标准,并分别使用5月、8月、11月和次年2月的SPEI-3来表征各个季节的干旱情况。而年尺度的干旱情况则使用每年12月的SPEI-12来表示。由于吐哈盆地的数据量较大,计算过程超出内存限制。因此,在本文中我们采取批量将PRE和ET0-PM的多波段数据按县进行掩膜,并将裁剪后的结果保存为对应县的数据。最后,为了将6个县(区)的SPEI计算结果汇总并进行分析,本文使用gdal.Warp()函数将各县的SPEI数据进行镶嵌,以便进行后续整体的干旱分析。

1. 单波段合成多波段

批量将1990-2022年吐哈盆地逐月降水量潜在蒸散发的单波段.tif数据合成多波段.tif

1990-2022年吐哈盆地1km逐月降水量的单波段数据
1990-2022年吐哈盆地1km逐月潜在蒸散量的单波段数据
# 批量将一个文件夹内的所有单波段栅格数据.tif合成一个多波段的.tif文件

# 导入需要的库
from osgeo import gdal  # 处理地理数据
import os  # 文件操作

# 根据自己的情况设置输入文件夹路径和输出文件路径
tifDir = r"D:\dissertation\SPEI\PRE"  
outtif = r"D:\dissertation\SPEI\PRE_199001-202212.tif"

# tifDir = r"D:\dissertation\SPEI\PET"  
# outtif = r"D:\dissertation\SPEI\PET_199001-202212.tif"

# 定义数据类型转换字典
NP2GDAL_CONVERSION = {
  "uint8": 1,
  "int8": 1,
  "uint16": 2,
  "int16": 3,
  "uint32": 4,
  "int32": 5,
  "float32": 6,
  "float64": 7,
  "complex64": 10,
  "complex128": 11,
}

# 使用列表推导式获取输入文件夹中以.tif结尾的文件列表
tifs = [i for i in os.listdir(tifDir) if i.endswith(".tif")] 

# 获取第一个.tif文件的投影、地理变换、列数、行数、数据类型等信息
bandsNum = len(tifs) # 获取波段数,即.tif文件的数量
dataset = gdal.Open(os.path.join(tifDir,tifs[0])) # 打开第一个.tif文件
projinfo = dataset.GetProjection() # 获取投影信息
geotransform = dataset.GetGeoTransform() # 获取地理变换信息
cols,rows=dataset.RasterXSize,dataset.RasterYSize # 获取列数和行数
datatype=dataset.GetRasterBand(1).ReadAsArray(0,0,1,1).dtype.name # 获取数据类型
gdaltype=NP2GDAL_CONVERSION[datatype] # 将数据类型转换为GDAL数据类型
dataset=None # 关闭第一个.tif文件

# 使用GDAL创建输出文件,并设置地理变换和投影信息
format = "GTiff" # 设置输出文件格式为.tif
driver = gdal.GetDriverByName(format) # 获取对应格式的驱动程序
dst_ds = driver.Create(outtif,cols, rows,bandsNum, gdaltype,options=['COMPRESS=LZW']) # 创建输出文件,设置列数、行数、波段数和数据类型,并添加压缩选项
dst_ds.SetGeoTransform(geotransform) # 设置输出文件的地理变换信息
dst_ds.SetProjection(projinfo) # 设置输出文件的投影信息

# 循环遍历每个.tif文件,将其数据写入到输出文件中对应的波段中
info = set() # 创建一个集合,用于存储每个.tif文件的列数和行数
for k in range(bandsNum): # 遍历每个.tif文件
    ds = gdal.Open(os.path.join(tifDir,tifs[k])) # 打开当前.tif文件
    X,Y = ds.RasterXSize,ds.RasterYSize # 获取当前.tif文件的列数和行数
    info.add("%s,%s"%(X,Y)) # 将列数和行数添加到集合中
    if(len(info) != 1): # 如果集合中的元素不唯一(即有.tif文件的列数或行数与其他文件不同)
        dst_ds = None # 关闭输出文件
        ds = None # 关闭当前.tif文件
        print("%s 列数行数不一样:%s,%s"%(tifs[k],X,Y)) # 打印出列数和行数不一致的.tif文件名和对应的列数和行数
        raise Exception("有影像行列数不一致") # 抛出异常,终止程序运行
    data = ds.GetRasterBand(1).ReadAsArray() # 读取当前.tif文件的第一个波段的数据
    ds = None # 关闭当前.tif文件
    dst_ds.GetRasterBand(k+1).WriteArray(data) # 将数据写入到输出文件的对应波段中
    dst_ds.GetRasterBand(k+1).SetDescription("hahha_%s"%k) # 设置输出文件的波段描述
    print("波段 %s ==> 文件 %s"%(k+1,tifs[k])) # 打印输出当前波段和对应的.tif文件名

# 关闭输出文件
dst_ds = None

2. 按县分割合成的多波段数据

1)利用ArcMap 10.8.2里“分割”工具批量生成吐哈各县(区)矢量数据

分割Split

2)按县掩膜前面合成的多波段栅格数据

# 按县掩膜合成的多波段栅格数据

# 导入需要的库
import os # 操作文件和文件夹
import rasterio # 处理栅格数据
import fiona # 处理矢量数据
from rasterio.mask import mask # 裁剪栅格数据

# 吐哈各县(区)矢量文件存放文件夹
vector_folder = "D:/dissertation/SPEI/County_Tuha"

# 多波段栅格文件路径
raster_file = "D:/dissertation/SPEI/PET_199001-202212.tif"
# raster_file = "D:/dissertation/SPEI/PRE_199001-202212.tif"

# 裁剪后的结果保存路径
output_folder = "D:/dissertation/SPEI/Mask_ComositeBands_PRE-PET"

# 遍历矢量文件夹中的所有矢量文件
for filename in os.listdir(vector_folder):
    if filename.endswith(".shp"): # 如果文件以.shp结尾(即矢量文件)        
        vector_file = os.path.join(vector_folder, filename) # 构建矢量文件路径      
        with rasterio.open(raster_file) as src: # 使用rasterio打开栅格文件            
            with fiona.open(vector_file, "r") as shapefile: # 使用fiona打开矢量文件
                shapes = [feature["geometry"] for feature in shapefile] # 获取矢量文件的几何形状
                out_image, out_transform = mask(src, shapes, crop=True) # 使用mask函数裁剪栅格数据
                out_meta = src.meta.copy() # 复制栅格数据的元数据
                
                # 更新元数据的变换参数
                out_meta.update({"driver": "GTiff",
                                 "height": out_image.shape[1],
                                 "width": out_image.shape[2],
                                 "transform": out_transform})
                
                # 构建结果保存文件路径
                output_file = os.path.join(output_folder, f"PET_199001-202212_{filename}.tif")
                # output_file = os.path.join(output_folder, f"PRE_199001-202212_{filename}.tif")
                
                # 保存裁剪后的栅格数据
                with rasterio.open(output_file, "w", **out_meta) as dest: # 使用rasterio创建并打开输出文件
                    dest.write(out_image) # 将裁剪后的栅格数据写入输出文件

3. 利用gma库计算SPEI

1)分县计算SPEI

# 使用gma库计算1990-2022年吐哈盆地标准化降水蒸散指数SPEI
# 参考:https://gma.luosgeo.com/UserGuide/climet/Index/SPEI.html
# 说明:整个吐哈盆地一起计算会超内存,所以分县计算

# 导入需要的库和模块
import numpy as np
import gma
from gma import io

# 读取月降水量和潜在蒸散量数据
PRESet = io.Open("D:/dissertation/SPEI/Mask_ComositeBands_PRE-PET/PRE_199001-202212_伊州区.shp.tif")
PETSet = io.Open("D:/dissertation/SPEI/Mask_ComositeBands_PRE-PET/PET_199001-202212_伊州区.shp.tif")

# 读取数据到数组
PRE = PRESet.ToArray()
PET = PETSet.ToArray()

# 读取的数据为三维数据(波段,行,列),第一维为时间序列(月数据),因此按照轴 0 来计算
# 时间尺度为SPEI-1、SPEI-3、SPEI-6、SPEI-12,分别对应月尺度、季尺度、半年和年尺度
SPEI1 = gma.climet.Index.SPEI(PRE, PET, Axis = 0)
SPEI3 = gma.climet.Index.SPEI(PRE, PET, Axis = 0, Scale = 3)
SPEI6 = gma.climet.Index.SPEI(PRE, PET, Axis = 0, Scale = 6)
SPEI12 = gma.climet.Index.SPEI(PRE, PET, Axis = 0, Scale = 12)

# 存储计算结果
S = [1,3,6,12]

for i in S:
    # 保存所有结果中的非全 nan 波段。即:去除时间尺度累积时序列前无效的波段。
    io.SaveArrayAsRaster(eval(f'SPEI{i}')[i-1:],
                         fr'D:\dissertation\SPEI\result\SPEI-{i}_1990-2022_伊州区.tif', 
                         Projection = PRESet.Projection,
                         Transform = PRESet.GeoTransform, 
                         DataType = 'Float32', 
                         NoData = np.nan)  

2)镶嵌合并为一个栅格

# 将各县计算的SPEI拼接为整个吐哈

# 导入所需的库
import os
from osgeo import gdal

# 设置输入文件夹和输出文件路径
input_folder = r'D:\dissertation\SPEI\result\SPEI-1'
output_file = r'D:\dissertation\SPEI\result\SPEI-1.tif'

# input_folder = r'D:\dissertation\SPEI\result\SPEI-3'
# output_file = r'D:\dissertation\SPEI\result\SPEI-3.tif'

# input_folder = r'D:\dissertation\SPEI\result\SPEI-6'
# output_file = r'D:\dissertation\SPEI\result\SPEI-6.tif'

# input_folder = r'D:\dissertation\SPEI\result\SPEI-12'
# output_file = r'D:\dissertation\SPEI\result\SPEI-12.tif'

# 获取输入文件夹中的所有.tif文件
input_files = [os.path.join(input_folder, f) for f in os.listdir(input_folder) if f.endswith('.tif')]

# 设置输出文件的空间参考和分辨率
output_projection = 'EPSG:4326'  # 设置输出文件的空间参考,这里使用WGS84经纬度坐标系
output_resolution = 0.0083333333  # 设置输出文件的分辨率,单位为度

# 使用gdal.Warp()函数进行镶嵌
gdal.Warp(output_file, input_files, format='GTiff', outputBounds=None, xRes=output_resolution, yRes=output_resolution, dstSRS=output_projection)

print("镶嵌完成!")

4. 将计算的SPEI分离成单波段

# 将SPEI多波段.tif文件分离成单波段数据,并保存为.tif格式的文件

# 导入需要的库
import numpy as np  # 处理数组和矩阵
import os  # 操作文件和目录
import random  # 生成随机数
from osgeo import gdal  # 取和写入地理空间数据
import tifffile as tif  # 读取和写入.tif格式文件
from skimage import data,exposure  # 图像处理
from sklearn import preprocessing  # 数据预处理

# 定义读取图像像素矩阵函数,其中fileName为图像文件名
def readTif(fileName):
    dataset = gdal.Open(fileName)  # 使用gdal库打开图像文件
    # width = dataset.RasterXSize
    # height = dataset.RasterYSize
    # GdalImg_data = dataset.ReadAsArray(0, 0, width, height)
    # return GdalImg_data
    return dataset  # 返回数据集对象

# 定义保存tif文件函数
def writeTiff(im_data,im_geotrans,im_proj,path):
    if 'int8' in im_data.dtype.name:  # 判断数据类型是否为int8
        datatype=gdal.GDT_Byte  # 设置数据类型为Byte
    elif 'int16' in im_data.dtype.name:  # 判断数据类型是否为int16
        datatype=gdal.GDT_UInt16  # 设置数据类型为UInt16
    else:
        datatype=gdal.GDT_Float32  # 设置数据类型为Float32
    if len(im_data.shape)==3:  # 判断数据维度是否为3
        im_bands,im_height,im_width=im_data.shape  # 获取波段数、高度和宽度
    elif len(im_data.shape)==2:  # 判断数据维度是否为2
        im_data=np.array([im_data])  # 将数据转换为3维数组
        im_bands,im_height,im_width=im_data.shape  # 获取波段数、高度和宽度
    driver=gdal.GetDriverByName("GTiff")  # 获取GTiff驱动
    dataset=driver.Create(path,int(im_width),int(im_height),int(im_bands),datatype)  # 创建.tif文件
    if(dataset!=None): 
        dataset.SetGeoTransform(im_geotrans)  # 写入仿射变换参数
        dataset.SetProjection(im_proj)  # 写入投影
    for i in range(im_bands):  # 遍历波段
        dataset.GetRasterBand(i+1).WriteArray(im_data[i])  # 将波段数据写入.tif文件
    del dataset  # 删除数据集对象

# 定义波段分离函数
def bandsclip(path1,path2):
    dataset_img=readTif(path1)  # 读取图像数据集
    width=dataset_img.RasterXSize  # 获取图像宽度
    height=dataset_img.RasterYSize  # 获取图像高度
    proj=dataset_img.GetProjection()  # 获取图像投影
    geotrans=dataset_img.GetGeoTransform()  # 获取图像仿射变换参数
    img=dataset_img.ReadAsArray(0,0,width,height)  # 读取图像数据

    img_out=[]  # 定义空列表
    for i in range(img.shape[0]):  # 遍历波段
        img_out=np.array(img[i,::])  # 获取当前波段数据
        writeTiff(img_out,geotrans,proj,path2+str(i)+'.tif')  # 将波段数据保存为.tif文件,文件名根据波段索引命名,出波段的名称命名格式可以修改,结合传递的path2参数

# 切换当前工作目录
os.chdir(r'D:/dissertation/SPEI/result')

path1=r'SPEI-1.tif'  # 要分离波段的原始图像数据名称
path2=r'./SPEI-1/SPEI-1_'  # 分离的各波段结果图像部分名称
bandsclip(path1,path2)  # 调用波段分离函数
print('Bandsclip END!')  # 打印输出
DIR *.*  /B >LIST.csv

后面就可以继续进行时空变化等的分析

  • 14
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值